Imported Upstream version 1.0.14
Sophie Brun
8 years ago
8 | 8 | |
9 | 9 | New features in the latest update |
10 | 10 | ===================================== |
11 | ||
12 | Sep 10, 2015: | |
13 | --- | |
14 | * Adding filename path information of report imported in history command | |
15 | * Remove old couchdb upgrade process | |
16 | * Adding Iceweasel browser > 38.2.0 support | |
17 | * Adding more navigability in differents GUI Web (Dashboard/Services/Views) | |
18 | * Fixed bug copy clipboard offline (update path of ngClip dependeces) | |
19 | * Added class to set colors to severities in new/edit vuln view | |
20 | * Medusa, Hydra & Metasploit plug-in now added discovered weak credentials as a vulnerability | |
21 | * Nmap plug-in applyies a severity depending on the result of a NSE script | |
22 | * Fixed small bug for empty ease of resolution | |
23 | * Adding more time to generation shells QT | |
24 | * Added "Search in Shodan" links in different views (Status Report, Host View, Service View) | |
25 | * Removed required of name field service bulk edition | |
26 | * Added ng-disabled on Edit button if select more of 1 host on Host View WEB UI | |
27 | * Refactored GUI Web: | |
28 | Icon added for Modal Error | |
29 | OS, Creator, Date for modal-services-by-host.html | |
30 | Fixed typo in Host Edit, the popup message was wrong | |
31 | First version for in estilos.css for clear mode | |
32 | Also, added hover to grey boxes in the Dashboard. | |
33 | * Added vulns count for Hosts in WEB UI | |
34 | * Updated w3af plugin to support report version 1.7.6 | |
35 | * Ignored cwe database from updater and QT views | |
36 | * Plugin for Nexpose XML Export 2.0 | |
37 | * Added masscan plugin (1.0.3) | |
11 | 38 | |
12 | 39 | Aug 19, 2015: |
13 | 40 | --- |
21 | 48 | * Clean dev log on plugins |
22 | 49 | * w3af plugin refactoring |
23 | 50 | * Fix Debian 7/8.1 install support |
24 | ||
25 | 51 | |
26 | 52 | Aug 05, 2015: |
27 | 53 | --- |
1 | 1 | <faraday> |
2 | 2 | |
3 | 3 | <appname>Faraday - Penetration Test IDE</appname> |
4 | <version>1.0.13</version> | |
4 | <version>1.0.14</version> | |
5 | 5 | <debug_status>0</debug_status> |
6 | 6 | <font>-Misc-Fixed-medium-r-normal-*-12-100-100-100-c-70-iso8859-1</font> |
7 | 7 | <home_path>~/</home_path> |
26 | 26 | CONST_FARADAY_BASE_CFG = "config/default.xml" |
27 | 27 | CONST_FARADAY_USER_CFG = "config/config.xml" |
28 | 28 | CONST_FARADAY_LIB_HELPERS = "shell/core/_helpers.so" |
29 | CONST_BLACKDBS = ('cwe','reports') | |
29 | 30 | |
30 | 31 | CONST_USER_HOME = "~" |
31 | 32 | CONST_USER_ZSHRC = "~/.zshrc" |
351 | 351 | |
352 | 352 | self.timer = qt.QTimer(self) |
353 | 353 | self.connect(self.timer, qt.SIGNAL('timeout()'), self.createShellTab) |
354 | self.timer.start(200, True) | |
354 | self.timer.start(1000, True) | |
355 | 355 | |
356 | 356 | for shell_widget in self._shell_widgets: |
357 | 357 | shell_widget.show() |
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 | import os | |
9 | import model.api | |
10 | import threading | |
11 | import time | |
12 | import traceback | |
13 | import re | |
14 | import requests | |
15 | try: | |
16 | import xml.etree.cElementTree as ET | |
17 | ||
18 | except ImportError: | |
19 | print "cElementTree could not be imported. Using ElementTree instead" | |
20 | import xml.etree.ElementTree as ET | |
21 | from apis.rest.api import PluginControllerAPIClient | |
22 | ||
23 | from utils.common import sha1OfFile | |
24 | ||
25 | from config.configuration import getInstanceConfiguration | |
26 | CONF = getInstanceConfiguration() | |
27 | ||
28 | class NoReportsWatchException(Exception): pass | |
29 | ||
30 | class ReportManager(threading.Thread): | |
31 | def __init__(self, timer, plugin_controller, path=None): | |
32 | threading.Thread.__init__(self) | |
33 | self.setDaemon(True) | |
34 | self.timer = timer | |
35 | self._stop = False | |
36 | self.path = path | |
37 | self.plugin_controller = plugin_controller | |
38 | self._report_path = None | |
39 | self._report_ppath = None | |
40 | ||
41 | def run(self): | |
42 | tmp_timer = 0 | |
43 | while not self._stop: | |
44 | ||
45 | time.sleep(1) | |
46 | tmp_timer += 1 | |
47 | if tmp_timer == self.timer: | |
48 | try: | |
49 | self.syncReports() | |
50 | except Exception: | |
51 | model.api.devlog("An exception was captured while saving reports\n%s" % traceback.format_exc()) | |
52 | finally: | |
53 | tmp_timer = 0 | |
54 | ||
55 | def stop(self): | |
56 | self._stop = True | |
57 | ||
58 | def watch(self, name): | |
59 | self._report_path = os.path.join(CONF.getReportPath(), name) | |
60 | self._report_ppath = os.path.join(self._report_path, "process") | |
61 | ||
62 | if not os.path.exists(self._report_path): | |
63 | os.mkdir(self._report_path) | |
64 | ||
65 | if not os.path.exists(self._report_ppath): | |
66 | os.mkdir(self._report_ppath) | |
67 | ||
68 | def startWatch(self): | |
69 | if not self._report_path: | |
70 | raise NoReportsWatchException() | |
71 | self.start() | |
72 | ||
73 | def syncReports(self): | |
74 | """ | |
75 | Synchronize report directory using the DataManager and Plugins online | |
76 | We first make sure that all shared reports were added to the repo | |
77 | """ | |
78 | ||
79 | for root, dirs, files in os.walk(self._report_path, False): | |
80 | ||
81 | if root == self._report_path: | |
82 | for name in files: | |
83 | filename = os.path.join(root, name) | |
84 | model.api.devlog( "Report file is %s" % filename) | |
85 | ||
86 | parser = ReportXmlParser(filename) | |
87 | if (parser.report_type is not None): | |
88 | #TODO: get host and port from config | |
89 | client = PluginControllerAPIClient("127.0.0.1", 9977) | |
90 | ||
91 | model.api.log("Importing report type: %s , (%s) - (%s)" % (parser.report_type, filename, sha1OfFile(filename))) | |
92 | ||
93 | command_string = "./%s report" % parser.report_type.lower() | |
94 | model.api.devlog("Executing %s" % (command_string)) | |
95 | ||
96 | new_cmd, output_file = client.send_cmd(command_string) | |
97 | client.send_output(command_string, filename) | |
98 | else: | |
99 | model.api.log("Report type not found: (%s)" % (filename)) | |
100 | ||
101 | os.rename(filename, os.path.join(self._report_ppath, name)) | |
102 | ||
103 | self.onlinePlugins() | |
104 | ||
105 | ||
106 | def onlinePlugins(self): | |
107 | """ | |
108 | Process online plugins | |
109 | """ | |
110 | _pluginsOn={"MetasploitOn" : "./metasploiton online",} | |
111 | _pluginsOn.update({"Beef" : "./beef online",}) | |
112 | _psettings=CONF.getPluginSettings() | |
113 | ||
114 | for k,v in _pluginsOn.iteritems(): | |
115 | if k in _psettings: | |
116 | if _psettings[k]['settings']['Enable'] == "1": | |
117 | new_cmd = self.plugin_controller.processCommandInput("", "", | |
118 | "", | |
119 | v, | |
120 | False) | |
121 | ||
122 | self.plugin_controller.storeCommandOutput("") | |
123 | ||
124 | self.plugin_controller.onCommandFinished() | |
125 | ||
126 | ||
127 | ||
128 | class ReportXmlParser(object): | |
129 | ||
130 | """Plugin that handles XML report files. | |
131 | ||
132 | :param xml_filepath: Xml file. | |
133 | ||
134 | :class:`.LoadReportXML` | |
135 | """ | |
136 | ||
137 | def __init__(self, xml_report_path): | |
138 | self.report_type = "" | |
139 | root_tag,output = self.getRootTag(xml_report_path) | |
140 | ||
141 | if root_tag: | |
142 | self.report_type = self.rType(root_tag,output) | |
143 | model.api.devlog(self.report_type) | |
144 | ||
145 | def getRootTag(self, xml_file_path): | |
146 | result = f = None | |
147 | try: | |
148 | f = open(xml_file_path, 'rb') | |
149 | try: | |
150 | for event, elem in ET.iterparse(f, ('start', )): | |
151 | result = elem.tag | |
152 | break | |
153 | except SyntaxError, err: | |
154 | self.report_type = None | |
155 | model.api.devlog("Not an xml file.\n %s" % (err)) | |
156 | ||
157 | except IOError, err: | |
158 | self.report_type = None | |
159 | model.api.devlog("Error while opening file.\n%s. %s" % (err, xml_file_path)) | |
160 | finally: | |
161 | f.seek(0) | |
162 | output=f.read() | |
163 | if f: f.close() | |
164 | ||
165 | return result,output | |
166 | ||
167 | def rType(self, tag, output): | |
168 | """Compares report root tag with known root tags. | |
169 | ||
170 | :param root_tag | |
171 | :rtype | |
172 | """ | |
173 | if "arachni_report" == tag: | |
174 | return "arachni" | |
175 | elif "nmaprun" == tag: | |
176 | return "nmap" | |
177 | elif "w3afrun" == tag: | |
178 | return "w3af" | |
179 | elif "NessusClientData_v2" == tag: | |
180 | return "nessus" | |
181 | elif "report" == tag: | |
182 | if re.search("alertitem",output) is None: | |
183 | return "openvas" | |
184 | else: | |
185 | return "zap" | |
186 | elif "niktoscan" == tag: | |
187 | return "nikto" | |
188 | elif "MetasploitV4" == tag: | |
189 | return "metasploit" | |
190 | elif "MetasploitV5" == tag: | |
191 | return "metasploit" | |
192 | elif "issues" == tag: | |
193 | return "burp" | |
194 | elif "OWASPZAPReport" == tag: | |
195 | return "zap" | |
196 | elif "ScanGroup" == tag: | |
197 | return "acunetix" | |
198 | elif "session" == tag: | |
199 | return "x1" | |
200 | elif "landscapePolicy" == tag: | |
201 | return "x1" | |
202 | elif "entities" == tag: | |
203 | return "impact" | |
204 | elif "NeXposeSimpleXML" == tag: | |
205 | return "nexpose" | |
206 | elif "SCAN" == tag: | |
207 | return "qualysguard" | |
208 | elif "scanJob" == tag: | |
209 | return "retina" | |
210 | elif "netsparker" == tag: | |
211 | return "netsparker" | |
212 | else: | |
213 | return None | |
214 | ||
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 | import os | |
9 | import model.api | |
10 | import threading | |
11 | import time | |
12 | import traceback | |
13 | import re | |
14 | import requests | |
15 | try: | |
16 | import xml.etree.cElementTree as ET | |
17 | ||
18 | except ImportError: | |
19 | print "cElementTree could not be imported. Using ElementTree instead" | |
20 | import xml.etree.ElementTree as ET | |
21 | from apis.rest.api import PluginControllerAPIClient | |
22 | ||
23 | from config.configuration import getInstanceConfiguration | |
24 | CONF = getInstanceConfiguration() | |
25 | ||
26 | class NoReportsWatchException(Exception): pass | |
27 | ||
28 | class ReportManager(threading.Thread): | |
29 | def __init__(self, timer, plugin_controller, path=None): | |
30 | threading.Thread.__init__(self) | |
31 | self.setDaemon(True) | |
32 | self.timer = timer | |
33 | self._stop = False | |
34 | self.path = path | |
35 | self.plugin_controller = plugin_controller | |
36 | self._report_path = None | |
37 | self._report_ppath = None | |
38 | ||
39 | def run(self): | |
40 | tmp_timer = 0 | |
41 | while not self._stop: | |
42 | ||
43 | time.sleep(1) | |
44 | tmp_timer += 1 | |
45 | if tmp_timer == self.timer: | |
46 | try: | |
47 | self.syncReports() | |
48 | except Exception: | |
49 | model.api.devlog("An exception was captured while saving reports\n%s" % traceback.format_exc()) | |
50 | finally: | |
51 | tmp_timer = 0 | |
52 | ||
53 | def stop(self): | |
54 | self._stop = True | |
55 | ||
56 | def watch(self, name): | |
57 | self._report_path = os.path.join(CONF.getReportPath(), name) | |
58 | self._report_ppath = os.path.join(self._report_path, "process") | |
59 | ||
60 | if not os.path.exists(self._report_path): | |
61 | os.mkdir(self._report_path) | |
62 | ||
63 | if not os.path.exists(self._report_ppath): | |
64 | os.mkdir(self._report_ppath) | |
65 | ||
66 | def startWatch(self): | |
67 | if not self._report_path: | |
68 | raise NoReportsWatchException() | |
69 | self.start() | |
70 | ||
71 | def syncReports(self): | |
72 | """ | |
73 | Synchronize report directory using the DataManager and Plugins online | |
74 | We first make sure that all shared reports were added to the repo | |
75 | """ | |
76 | ||
77 | for root, dirs, files in os.walk(self._report_path, False): | |
78 | ||
79 | if root == self._report_path: | |
80 | for name in files: | |
81 | filename = os.path.join(root, name) | |
82 | model.api.devlog( "Report file is %s" % filename) | |
83 | ||
84 | parser = ReportXmlParser(filename) | |
85 | if (parser.report_type is not None): | |
86 | #TODO: get host and port from config | |
87 | client = PluginControllerAPIClient("127.0.0.1", 9977) | |
88 | ||
89 | model.api.devlog("The file is %s, %s" % (filename,parser.report_type)) | |
90 | ||
91 | command_string = "./%s %s" % (parser.report_type.lower(), filename) | |
92 | model.api.devlog("Executing %s" % (command_string)) | |
93 | ||
94 | new_cmd, output_file = client.send_cmd(command_string) | |
95 | client.send_output(command_string, filename) | |
96 | os.rename(filename, os.path.join(self._report_ppath, name)) | |
97 | ||
98 | self.onlinePlugins() | |
99 | ||
100 | ||
101 | def onlinePlugins(self): | |
102 | """ | |
103 | Process online plugins | |
104 | """ | |
105 | _pluginsOn={"MetasploitOn" : "./metasploiton online",} | |
106 | _pluginsOn.update({"Beef" : "./beef online",}) | |
107 | _psettings=CONF.getPluginSettings() | |
108 | ||
109 | for k,v in _pluginsOn.iteritems(): | |
110 | if k in _psettings: | |
111 | if _psettings[k]['settings']['Enable'] == "1": | |
112 | new_cmd = self.plugin_controller.processCommandInput("", "", | |
113 | "", | |
114 | v, | |
115 | False) | |
116 | ||
117 | self.plugin_controller.storeCommandOutput("") | |
118 | ||
119 | self.plugin_controller.onCommandFinished() | |
120 | ||
121 | ||
122 | ||
123 | class ReportXmlParser(object): | |
124 | ||
125 | """Plugin that handles XML report files. | |
126 | ||
127 | :param xml_filepath: Xml file. | |
128 | ||
129 | :class:`.LoadReportXML` | |
130 | """ | |
131 | ||
132 | def __init__(self, xml_report_path): | |
133 | self.report_type = "" | |
134 | root_tag,output = self.getRootTag(xml_report_path) | |
135 | ||
136 | if root_tag: | |
137 | self.report_type = self.rType(root_tag,output) | |
138 | model.api.devlog(self.report_type) | |
139 | ||
140 | def getRootTag(self, xml_file_path): | |
141 | result = f = None | |
142 | try: | |
143 | f = open(xml_file_path, 'rb') | |
144 | try: | |
145 | for event, elem in ET.iterparse(f, ('start', )): | |
146 | result = elem.tag | |
147 | break | |
148 | except SyntaxError, err: | |
149 | self.report_type = None | |
150 | model.api.devlog("Not an xml file.\n %s" % (err)) | |
151 | ||
152 | except IOError, err: | |
153 | self.report_type = None | |
154 | model.api.devlog("Error while opening file.\n%s. %s" % (err, xml_file_path)) | |
155 | finally: | |
156 | f.seek(0) | |
157 | output=f.read() | |
158 | if f: f.close() | |
159 | ||
160 | return result,output | |
161 | ||
162 | def rType(self, tag, output): | |
163 | """Compares report root tag with known root tags. | |
164 | ||
165 | :param root_tag | |
166 | :rtype | |
167 | """ | |
168 | if "arachni_report" == tag: | |
169 | return "arachni" | |
170 | elif "nmaprun" == tag: | |
171 | return "nmap" | |
172 | elif "w3af-run" == tag: | |
173 | return "w3af" | |
174 | elif "NessusClientData_v2" == tag: | |
175 | return "nessus" | |
176 | elif "report" == tag: | |
177 | if re.search("alertitem",output) is None: | |
178 | return "openvas" | |
179 | else: | |
180 | return "zap" | |
181 | elif "niktoscan" == tag: | |
182 | return "nikto" | |
183 | elif "MetasploitV4" == tag: | |
184 | return "metasploit" | |
185 | elif "MetasploitV5" == tag: | |
186 | return "metasploit" | |
187 | elif "issues" == tag: | |
188 | return "burp" | |
189 | elif "OWASPZAPReport" == tag: | |
190 | return "zap" | |
191 | elif "ScanGroup" == tag: | |
192 | return "acunetix" | |
193 | elif "session" == tag: | |
194 | return "x1" | |
195 | elif "landscapePolicy" == tag: | |
196 | return "x1" | |
197 | elif "entities" == tag: | |
198 | return "impact" | |
199 | elif "NeXposeSimpleXML" == tag: | |
200 | return "nexpose" | |
201 | elif "NexposeReport" == tag: | |
202 | return "nexpose-full" | |
203 | elif "SCAN" == tag: | |
204 | return "qualysguard" | |
205 | elif "scanJob" == tag: | |
206 | return "retina" | |
207 | elif "netsparker" == tag: | |
208 | return "netsparker" | |
209 | else: | |
210 | return None |
18 | 18 | from managers.all import ViewsManager |
19 | 19 | |
20 | 20 | #from persistence.change import change_factory |
21 | ||
21 | from config.globals import CONST_BLACKDBS | |
22 | 22 | from config.configuration import getInstanceConfiguration |
23 | 23 | CONF = getInstanceConfiguration() |
24 | 24 | |
522 | 522 | |
523 | 523 | #@trap_timeout |
524 | 524 | def _loadDbs(self): |
525 | conditions = lambda x: not x.startswith("_") and x != 'reports' | |
525 | conditions = lambda x: not x.startswith("_") and x not in CONST_BLACKDBS | |
526 | 526 | for dbname in filter(conditions, self.__serv.all_dbs()): |
527 | 527 | if dbname not in self.dbs.keys(): |
528 | 528 | getLogger(self).debug( |
26 | 26 | __email__ = "[email protected]" |
27 | 27 | __status__ = "Development" |
28 | 28 | |
29 | ||
30 | ||
31 | ||
29 | ||
30 | ||
31 | ||
32 | 32 | |
33 | 33 | class HydraParser(object): |
34 | 34 | """ |
43 | 43 | reg = re.search("\[([^$]+)\]\[([^$]+)\] host: ([^$]+) login: ([^$]+) password: ([^$]+)",l) |
44 | 44 | if reg: |
45 | 45 | item = {'port' : reg.group(1), 'plugin' : reg.group(2), 'ip' : reg.group(3), 'login' : reg.group(4), 'password' : reg.group(5) } |
46 | ||
46 | ||
47 | 47 | self.items.append(item) |
48 | 48 | |
49 | ||
49 | ||
50 | 50 | |
51 | 51 | |
52 | 52 | |
87 | 87 | "-6":"prefer IPv6 addresses", |
88 | 88 | "-v":"verbose mode / show login+pass combination for each attempt", |
89 | 89 | "-V":"verbose mode / show login+pass combination for each attempt", |
90 | "-U":"service module usage details", | |
90 | "-U":"service module usage details", | |
91 | 91 | } |
92 | 92 | |
93 | 93 | global current_path |
94 | 94 | self._output_file_path = os.path.join(self.data_path, |
95 | 95 | "hydra_output-%s.txt" % self._rid) |
96 | ||
97 | ||
96 | ||
97 | ||
98 | 98 | |
99 | 99 | |
100 | 100 | def parseOutputString(self, output, debug = False): |
115 | 115 | for item in parser.items: |
116 | 116 | service=item['plugin'] |
117 | 117 | port=item['port'] |
118 | ||
118 | ||
119 | 119 | if hosts.has_key(item['ip']) == False: |
120 | 120 | hosts[item['ip']]=[] |
121 | ||
121 | ||
122 | 122 | hosts[item['ip']].append([item['login'],item['password']]) |
123 | ||
123 | ||
124 | 124 | for k,v in hosts.iteritems(): |
125 | 125 | h_id = self.createAndAddHost(k) |
126 | 126 | if self._isIPV4(k): |
130 | 130 | s_id = self.createAndAddServiceToInterface(h_id,i_id,service,ports=[port],protocol="tcp",status="open") |
131 | 131 | for cred in v: |
132 | 132 | self.createAndAddCredToService(h_id,s_id, cred[0],cred[1]) |
133 | ||
133 | self.createAndAddVulnToService(h_id, s_id, "Weak Credentials","[hydra found the following credentials]\nuser:%s\npass:%s" % (cred[0], cred[1]), severity="high") | |
134 | ||
134 | 135 | del parser |
135 | 136 | |
136 | 137 | xml_arg_re = re.compile(r"^.*(-o\s*[^\s]+).*$") |
137 | 138 | def processCommandString(self, username, current_path, command_string): |
138 | 139 | |
139 | self._output_file_path=os.path.join(self.data_path,"hydra_output-%s.txt" % random.uniform(1,10)) | |
140 | self._output_file_path=os.path.join(self.data_path,"hydra_output-%s.txt" % random.uniform(1,10)) | |
140 | 141 | arg_match = self.xml_arg_re.match(command_string) |
141 | 142 | |
142 | 143 | if arg_match is None: |
145 | 146 | return re.sub(arg_match.group(1), |
146 | 147 | r"-o %s" % self._output_file_path, |
147 | 148 | command_string) |
148 | ||
149 | ||
149 | 150 | def _isIPV4(self, ip): |
150 | 151 | if len(ip.split(".")) == 4: |
151 | 152 | return True |
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 |
0 | ''' | |
1 | Faraday Penetration Test IDE | |
2 | Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/) | |
3 | See the file 'doc/LICENSE' for the license information | |
4 | ||
5 | ''' | |
6 | from plugins.repo.nmap.plugin import NmapPlugin | |
7 | import os | |
8 | import re | |
9 | import random | |
10 | ||
11 | current_path = os.path.abspath(os.getcwd()) | |
12 | ||
13 | ||
14 | class CmdMasscanPlugin(NmapPlugin): | |
15 | """ | |
16 | Example plugin to parse amap output. | |
17 | """ | |
18 | def __init__(self): | |
19 | NmapPlugin.__init__(self) | |
20 | self.id = "Masscan" | |
21 | self.name = "Masscan Output Plugin" | |
22 | self.plugin_version = "0.0.1" | |
23 | self.version = "1.0.3" | |
24 | self.options = None | |
25 | self._command_regex = re.compile(r'^(masscan|sudo masscan|\.\/masscan|sudo \.\/masscan).*?') | |
26 | self._output_file_path = os.path.join(self.data_path, | |
27 | "masscan_output-%s.xml" % self._rid) | |
28 | ||
29 | def processCommandString(self, username, current_path, command_string): | |
30 | """ | |
31 | Adds the -oX parameter to get xml output to the command string that the | |
32 | user has set. | |
33 | """ | |
34 | self._output_file_path = os.path.join(self.data_path,"masscan_output-%s.xml" % random.uniform(1,10)) | |
35 | ||
36 | arg_match = self.xml_arg_re.match(command_string) | |
37 | ||
38 | ||
39 | if arg_match is None: | |
40 | return re.sub(r"(^.*?masscan)", | |
41 | r"\1 -oX %s" % self._output_file_path, | |
42 | command_string) | |
43 | else: | |
44 | return re.sub(arg_match.group(1), | |
45 | r"-oX %s" % self._output_file_path, | |
46 | command_string) | |
47 | ||
48 | ||
49 | def createPlugin(): | |
50 | return CmdMasscanPlugin() |
150 | 150 | s_id = self.createAndAddServiceToInterface(h_id,i_id,item['service'],ports=[port],protocol="tcp",status="open") |
151 | 151 | |
152 | 152 | self.createAndAddCredToService(h_id,s_id, item['user'],item['pass']) |
153 | ||
153 | self.createAndAddVulnToService(h_id, s_id, "Weak Credentials","[medusa found the following credentials]\nuser:%s\npass:%s" % (item['user'], item['pass']), severity="high") | |
154 | 154 | del parser |
155 | 155 | |
156 | 156 | xml_arg_re = re.compile(r"^.*(-O\s*[^\s]+).*$") |
388 | 388 | if s['port'] in item.credsByService: |
389 | 389 | for c in item.credsByService[s['port']]: |
390 | 390 | self.createAndAddCredToService(h_id,s_id,c.user,c.passwd) |
391 | self.createAndAddVulnToService(h_id, s_id, "Weak Credentials","[metasploit found the following credentials]\nuser:%s\npass:%s" % (c.user, c.passwd), severity="high") | |
391 | 392 | |
392 | 393 | n_id = None |
393 | 394 | for v in item.vulnsByService[s['id']]: |
194 | 194 | for c in cur.fetchall(): |
195 | 195 | self._checkDate(str(c[3])) |
196 | 196 | self.createAndAddCredToService(h_id,s_id,c[4],c[5]) |
197 | self.createAndAddVulnToService(h_id, s_id, "Weak Credentials","[metasploit found the following credentials]\nuser:%s\npass:%s" % (c[4], c[5]), severity="high") | |
197 | 198 | |
198 | 199 | |
199 | 200 |
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 |
0 | #!/usr/bin/env python | |
1 | # -*- coding: utf-8 -*- | |
2 | ||
3 | ''' | |
4 | Faraday Penetration Test IDE | |
5 | Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) | |
6 | See the file 'doc/LICENSE' for the license information | |
7 | ||
8 | ''' | |
9 | from __future__ import with_statement | |
10 | from plugins import core | |
11 | from model import api | |
12 | import re | |
13 | import os | |
14 | import pprint | |
15 | import sys | |
16 | ||
17 | try: | |
18 | import xml.etree.cElementTree as ET | |
19 | import xml.etree.ElementTree as ET_ORIG | |
20 | ETREE_VERSION = ET_ORIG.VERSION | |
21 | except ImportError: | |
22 | import xml.etree.ElementTree as ET | |
23 | ETREE_VERSION = ET.VERSION | |
24 | ||
25 | ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] | |
26 | ||
27 | current_path = os.path.abspath(os.getcwd()) | |
28 | ||
29 | __author__ = "Micaela Ranea Sanchez" | |
30 | __copyright__ = "Copyright (c) 2013, Infobyte LLC" | |
31 | __credits__ = ["Francisco Amato", "Federico Kirschbaum", "Micaela Ranea Sanchez", "German Riera"] | |
32 | __license__ = "" | |
33 | __version__ = "1.0.0" | |
34 | __maintainer__ = "Micaela Ranea Sanchez" | |
35 | __email__ = "[email protected]" | |
36 | __status__ = "Development" | |
37 | ||
38 | ||
39 | class NexposeFullXmlParser(object): | |
40 | """ | |
41 | The objective of this class is to parse Nexpose's XML 2.0 Report. | |
42 | ||
43 | TODO: Handle errors. | |
44 | TODO: Test nexpose output version. Handle what happens if the parser doesn't support it. | |
45 | TODO: Test cases. | |
46 | ||
47 | @param xml_filepath A proper xml generated by nexpose | |
48 | """ | |
49 | def __init__(self, xml_output): | |
50 | tree = self.parse_xml(xml_output) | |
51 | vulns = self.get_vuln_definitions(tree) | |
52 | ||
53 | if tree: | |
54 | self.items = self.get_items(tree, vulns) | |
55 | else: | |
56 | self.items = [] | |
57 | ||
58 | def parse_xml(self, xml_output): | |
59 | """ | |
60 | Open and parse an xml file. | |
61 | ||
62 | TODO: Write custom parser to just read the nodes that we need instead of | |
63 | reading the whole file. | |
64 | ||
65 | @return xml_tree An xml tree instance. None if error. | |
66 | """ | |
67 | try: | |
68 | tree = ET.fromstring(xml_output) | |
69 | except SyntaxError, err: | |
70 | print "SyntaxError: %s. %s" % (err, xml_output) | |
71 | return None | |
72 | ||
73 | return tree | |
74 | ||
75 | def parse_html_type(self, node): | |
76 | """ | |
77 | Parse XML element of type HtmlType | |
78 | ||
79 | @return ret A string containing the parsed element | |
80 | """ | |
81 | ret = "" | |
82 | tag = node.tag.lower() | |
83 | ||
84 | if tag == 'containerblockelement': | |
85 | if len(list(node)) > 0: | |
86 | for child in list(node): | |
87 | ret += self.parse_html_type(child) | |
88 | else: | |
89 | ret += str(node.text).strip() | |
90 | if tag == 'listitem': | |
91 | if len(list(node)) > 0: | |
92 | for child in list(node): | |
93 | ret += self.parse_html_type(child) | |
94 | else: | |
95 | ret = str(node.text).strip() | |
96 | if tag == 'orderedlist': | |
97 | i = 1 | |
98 | for item in list(node): | |
99 | ret += "\t" + str(i) + " " + self.parse_html_type(item) + "\n" | |
100 | i += 1 | |
101 | if tag == 'paragraph': | |
102 | if len(list(node)) > 0: | |
103 | for child in list(node): | |
104 | ret += self.parse_html_type(child) | |
105 | else: | |
106 | ret += str(node.text).strip() | |
107 | if tag == 'unorderedlist': | |
108 | for item in list(node): | |
109 | ret += "\t" + "* " + self.parse_html_type(item) + "\n" | |
110 | if tag == 'urllink': | |
111 | if node.text: | |
112 | ret += str(node.text).strip() + " " | |
113 | last = "" | |
114 | for attr in node.attrib: | |
115 | if node.get(attr) != node.get(last): | |
116 | ret += str(node.get(attr)) + " " | |
117 | last = attr | |
118 | ||
119 | return ret | |
120 | ||
121 | def parse_tests_type(self, node, vulnsDefinitions): | |
122 | """ | |
123 | Parse XML element of type TestsType | |
124 | ||
125 | @return vulns A list of vulnerabilities according to vulnsDefinitions | |
126 | """ | |
127 | vulns = list() | |
128 | ||
129 | for tests in node.iter('tests'): | |
130 | for test in tests.iter('test'): | |
131 | vuln = dict() | |
132 | if test.get('id').lower() in vulnsDefinitions: | |
133 | vuln = vulnsDefinitions[test.get('id').lower()] | |
134 | for desc in list(test): | |
135 | vuln['desc'] += self.parse_html_type(desc) | |
136 | vulns.append(vuln) | |
137 | ||
138 | return vulns | |
139 | ||
140 | def get_vuln_definitions(self, tree): | |
141 | """ | |
142 | @returns vulns A dict of Vulnerability Definitions | |
143 | """ | |
144 | vulns = dict() | |
145 | ||
146 | for vulnsDef in tree.iter('VulnerabilityDefinitions'): | |
147 | for vulnDef in vulnsDef.iter('vulnerability'): | |
148 | vid = vulnDef.get('id').lower() | |
149 | vector = vulnDef.get('cvssVector') | |
150 | ||
151 | vuln = { | |
152 | 'desc': "", | |
153 | 'name': vulnDef.get('title'), | |
154 | 'refs': [ "vector: " + vector, vid], | |
155 | 'resolution': "", | |
156 | 'severity': (int(vulnDef.get('severity'))-1)/2, | |
157 | 'tags': list() | |
158 | } | |
159 | ||
160 | for item in list(vulnDef): | |
161 | if item.tag == 'description': | |
162 | for htmlType in list(item): | |
163 | vuln['desc'] += self.parse_html_type(htmlType) | |
164 | if item.tag == 'exploits': | |
165 | for exploit in list(item): | |
166 | vuln['refs'].append(str(exploit.get('title')).strip() + ' ' + str(exploit.get('link')).strip()) | |
167 | if item.tag == 'references': | |
168 | for ref in list(item): | |
169 | vuln['refs'].append(str(ref.text).strip()) | |
170 | if item.tag == 'solution': | |
171 | for htmlType in list(item): | |
172 | vuln['resolution'] += self.parse_html_type(htmlType) | |
173 | """ | |
174 | # there is currently no method to register tags in vulns | |
175 | if item.tag == 'tags': | |
176 | for tag in list(item): | |
177 | vuln['tags'].append(tag.text.lower()) | |
178 | """ | |
179 | vulns[vid] = vuln | |
180 | ||
181 | return vulns | |
182 | ||
183 | def get_items(self, tree, vulns): | |
184 | """ | |
185 | @return hosts A list of Host instances | |
186 | """ | |
187 | ||
188 | hosts = list() | |
189 | ||
190 | for nodes in tree.iter('nodes'): | |
191 | for node in nodes.iter('node'): | |
192 | host = dict() | |
193 | host['name'] = node.get('address') | |
194 | host['hostnames'] = set() | |
195 | host['os'] = "" | |
196 | host['services'] = list() | |
197 | host['vulns'] = self.parse_tests_type(node, vulns) | |
198 | ||
199 | for names in node.iter('names'): | |
200 | for name in list(names): | |
201 | host['hostnames'].add(name.text) | |
202 | ||
203 | for fingerprints in node.iter('fingerprints'): | |
204 | os = fingerprints.find('os') | |
205 | if os is not None: | |
206 | host['os'] = os.get('product', "") | |
207 | if os.get('version') is not None: | |
208 | host['os'] += " " + os.get('version') | |
209 | ||
210 | for endpoints in node.iter('endpoints'): | |
211 | for endpoint in list(endpoints): | |
212 | svc = { | |
213 | 'protocol': endpoint.get('protocol'), | |
214 | 'port': endpoint.get('port'), | |
215 | 'status': endpoint.get('status'), | |
216 | } | |
217 | for services in endpoint.iter('services'): | |
218 | for service in list(services): | |
219 | svc['name'] = service.get('name') | |
220 | svc['vulns'] = self.parse_tests_type(service, vulns) | |
221 | for configs in service.iter('configurations'): | |
222 | for config in list(configs): | |
223 | if "banner" in config.get('name'): | |
224 | svc['version'] = config.get('name') | |
225 | ||
226 | host['services'].append(svc) | |
227 | ||
228 | hosts.append(host) | |
229 | ||
230 | return hosts | |
231 | ||
232 | class NexposeFullPlugin(core.PluginBase): | |
233 | """ | |
234 | Example plugin to parse nexpose output. | |
235 | """ | |
236 | def __init__(self): | |
237 | core.PluginBase.__init__(self) | |
238 | self.id = "NexposeFull" | |
239 | self.name = "Nexpose XML 2.0 Report Plugin" | |
240 | self.plugin_version = "0.0.1" | |
241 | self.version = "Nexpose Enterprise 5.7.19" | |
242 | self.framework_version = "1.0.0" | |
243 | self.options = None | |
244 | self._current_output = None | |
245 | self._command_regex = re.compile(r'^(sudo nexpose|\.\/nexpose).*?') | |
246 | ||
247 | global current_path | |
248 | self._output_file_path = os.path.join(self.data_path, | |
249 | "nexpose_full_output-%s.xml" % self._rid) | |
250 | ||
251 | def parseOutputString(self, output, debug = False): | |
252 | parser = NexposeFullXmlParser(output) | |
253 | ||
254 | for item in parser.items: | |
255 | h_id = self.createAndAddHost(item['name'], item['os']) | |
256 | i_id = self.createAndAddInterface(h_id, item['name'], ipv4_address = item['name'], hostname_resolution = item['hostnames']) | |
257 | ||
258 | for v in item['vulns']: | |
259 | v_id = self.createAndAddVulnToHost(h_id, v['name'], v['desc'], v['refs'], v['severity'], v['resolution']) | |
260 | ||
261 | for s in item['services']: | |
262 | web = False | |
263 | version = s.get("version", "") | |
264 | ||
265 | s_id = self.createAndAddServiceToInterface(h_id, i_id, s['name'], | |
266 | s['protocol'], | |
267 | ports = [str(s['port'])], | |
268 | status = s['status'], | |
269 | version = version) | |
270 | for v in s['vulns']: | |
271 | v_id = self.createAndAddVulnToService(h_id, s_id, v['name'], v['desc'], v['refs'], v['severity'], v['resolution']) | |
272 | del parser | |
273 | ||
274 | def processCommandString(self, username, current_path, command_string): | |
275 | return None | |
276 | ||
277 | ||
278 | def setHost(self): | |
279 | pass | |
280 | ||
281 | ||
282 | def createPlugin(): | |
283 | return NexposeFullPlugin() | |
284 | ||
285 | if __name__ == '__main__': | |
286 | parser = NexposeFullXmlParser(sys.argv[1]) | |
287 | for item in parser.items: | |
288 | if item.status == 'up': | |
289 | print item |
538 | 538 | description = srvname) |
539 | 539 | note=True |
540 | 540 | for v in port.vulns: |
541 | severity = 0 | |
541 | 542 | desc=v.desc |
542 | 543 | desc+="\nOutput: "+ v.response if v.response else "" |
544 | ||
545 | if re.search(r"VULNERABLE",desc): | |
546 | severity = "high" | |
547 | if re.search(r"ERROR",desc): | |
548 | severity = "unclassified" | |
549 | if re.search(r"Couldn't",desc): | |
550 | severity = "unclassified" | |
543 | 551 | if v.web: |
544 | 552 | if note: |
545 | 553 | n_id = self.createAndAddNoteToService(h_id,s_id,"website","") |
546 | 554 | n2_id = self.createAndAddNoteToNote(h_id,s_id,n_id,minterfase,"") |
547 | 555 | note=False |
548 | 556 | v_id = self.createAndAddVulnWebToService(h_id,s_id,v.name,desc=desc, |
549 | severity=0,website=minterfase) | |
557 | severity=severity,website=minterfase) | |
550 | 558 | else: |
551 | 559 | v_id = self.createAndAddVulnToService(h_id,s_id,v.name,desc=v.desc, |
552 | severity=0) | |
560 | severity=severity) | |
553 | 561 | |
554 | 562 | del parser |
555 | 563 |
86 | 86 | bugtype="" |
87 | 87 | |
88 | 88 | |
89 | scaninfo = tree.findall('scaninfo')[0] | |
89 | scaninfo = tree.findall('scan-info')[0] | |
90 | 90 | self.target = scaninfo.get('target') |
91 | 91 | host = re.search("(http|https|ftp)\://([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&%\$\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))[\:]*([0-9]+)*([/]*($|[a-zA-Z0-9\.\,\?\'\\\+&%\$#\=~_\-]+)).*?$", self.target) |
92 | 92 | |
153 | 153 | self.id=self.node.get('id') |
154 | 154 | self.name = self.node.get('name') |
155 | 155 | self.url = self.node.get('url') |
156 | self.url = self.url if self.url != 'None' else "/" | |
156 | 157 | self.plugin = self.node.get('plugin') |
157 | 158 | self.detail = self.get_text_from_subnode('description') |
159 | self.resolution = self.get_text_from_subnode('fix-guidance') | |
160 | self.fix_effort = self.get_text_from_subnode('fix-effort') | |
161 | self.longdetail = self.get_text_from_subnode('description') | |
158 | 162 | self.severity = self.node.get('severity') |
159 | 163 | self.method = self.node.get('method') |
164 | self.ref = [] | |
160 | 165 | self.param = self.node.get('var') if self.node.get('var') != "None" else "" |
166 | for ref in self.node.findall('references/reference'): | |
167 | self.ref.append(ref.get('url')) | |
168 | ||
161 | 169 | self.req = self.resp = '' |
162 | 170 | for tx in self.node.findall('http-transactions/http-transaction'): |
163 | ||
164 | self.req = tx.find('httprequest/status').text | |
165 | ||
166 | self.resp = tx.find('httpresponse/status').text | |
171 | self.req = tx.find('http-request/status').text | |
172 | for h in tx.findall('http-request/headers/header'): | |
173 | self.req += "\n%s: %s" % (h.get('field'),h.get('content')) | |
174 | ||
175 | self.resp = tx.find('http-response/status').text | |
176 | for h in tx.findall('http-response/headers/header'): | |
177 | self.resp += "\n%s: %s" % (h.get('field'),h.get('content')) | |
178 | self.resp += "\n%s" % tx.find('http-response/body').text | |
179 | ||
167 | 180 | |
168 | 181 | def do_clean(self,value): |
169 | 182 | myreturn ="" |
193 | 206 | core.PluginBase.__init__(self) |
194 | 207 | self.id = "W3af" |
195 | 208 | self.name = "W3af XML Output Plugin" |
196 | self.plugin_version = "0.0.1" | |
197 | self.version = "1.2" | |
209 | self.plugin_version = "0.0.2" | |
210 | self.version = "1.7.6" | |
198 | 211 | self.framework_version = "1.0.0" |
199 | 212 | self.options = None |
200 | 213 | self._current_output = None |
203 | 216 | self._completition = { |
204 | 217 | "":"", |
205 | 218 | "-h":"Display this help message.", |
206 | "-p":"-p <profile> Run with the selected <profile>", | |
207 | 219 | } |
208 | 220 | |
209 | 221 | global current_path |
229 | 241 | for item in parser.items: |
230 | 242 | v_id = self.createAndAddVulnWebToService(h_id, s_id, item.name, |
231 | 243 | item.detail, pname=item.param, path=item.url, website=parser.host, severity=item.severity, |
232 | method=item.method, request=item.req, response=item.resp) | |
244 | method=item.method, request=item.req, resolution=item.resolution, ref=item.ref, response=item.resp) | |
233 | 245 | del parser |
234 | 246 | |
235 | 247 |
36 | 36 | logger.info('Installing missing dependencies in pip') |
37 | 37 | pip.main(['install', '-r', CONST_REQUIREMENTS_FILE, '--user']) |
38 | 38 | |
39 | logger.info('Upgrading DBs to latest version') | |
40 | DB().run() | |
39 | # logger.info('Upgrading DBs to latest version') | |
40 | # DB().run() | |
41 | 41 | |
42 | 42 | logger.info('Upgrading DBs to latest version') |
43 | 43 | CouchViews().run() |
79 | 79 | # if not query_yes_no("Faraday won't behave correctly with older versions, proceed?", 'no'): |
80 | 80 | # return |
81 | 81 | |
82 | dbs = filter(lambda x: not x.startswith("_") and 'backup' not in x and \ | |
83 | 'reports' not in x, serv.all_dbs()) | |
82 | dbs = filter(lambda x: not x.startswith("_") and 'backup' not in x and x not in CONST_BLACKDBS, serv.all_dbs()) | |
84 | 83 | logger.info('Dbs to upgrade: %s' % (', '.join(dbs))) |
85 | 84 | |
86 | 85 | |
172 | 171 | serv = couchdbkit.Server(source_server) |
173 | 172 | |
174 | 173 | logger.info('We are about to upgrade dbs in Server [%s]' % source_server) |
175 | dbs = filter(lambda x: not x.startswith("_") and 'backup' not in x and 'reports' not in x, serv.all_dbs()) | |
174 | dbs = filter(lambda x: not x.startswith("_") and 'backup' not in x and x not in CONST_BLACKDBS, serv.all_dbs()) | |
176 | 175 | logger.info('Dbs to upgrade: %s' % (', '.join(dbs))) |
177 | 176 | |
178 | 177 | if not query_yes_no('Proceed?', 'no'): |
1 | 1 | /* Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) */ |
2 | 2 | /* See the file 'doc/LICENSE' for the license information */ |
3 | 3 | /*reset*/ |
4 | html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td {margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}body {line-height:1}ol,ul {list-style:none}blockquote,q {quotes:none}:focus {outline:0}ins {text-decoration:none}del {text-decoration:line-through}table {border-collapse:collapse;border-spacing:0}ul,li {list-style:none}.clearfix:after{content:".";display:block;clear:both;visibility:hidden;line-height:0; height:0} | |
4 | html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td {margin:0;padding:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}body {line-height:1}ol,ul {list-style:none}blockquote,q {quotes:none}:focus {outline:0}ins {text-decoration:none}del {text-decoration:line-through}table {border-collapse:collapse;border-spacing:0}ul,li {list-style:none}.clearfix:after{content:".";display:block;clear:both;visibility:hidden;line-height:0; height:0} | |
5 | 5 | .clearfix{display:inline-block}html[xmlns] .clearfix{display:block}* html .clearfix{height:1%} |
6 | 6 | /**/ |
7 | @font-face { | |
8 | font-family: 'Ubuntu'; | |
9 | font-style: normal; | |
10 | font-weight: 300; | |
11 | src: local('Ubuntu Light'), local('Ubuntu-Light'), url(fonts/ubuntu/_aijTyevf54tkVDLy-dlnLO3LdcAZYWl9Si6vvxL-qU.woff) format('woff'); | |
12 | } | |
13 | @font-face { | |
14 | font-family: 'Ubuntu'; | |
15 | font-style: normal; | |
16 | font-weight: 400; | |
17 | src: local('Ubuntu'), url(fonts/ubuntu/vRvZYZlUaogOuHbBTT1SNevvDin1pK8aKteLpeZ5c0A.woff) format('woff'); | |
18 | } | |
19 | @font-face { | |
20 | font-family: 'Ubuntu'; | |
21 | font-style: normal; | |
22 | font-weight: 700; | |
23 | src: local('Ubuntu Bold'), local('Ubuntu-Bold'), url(fonts/ubuntu/0ihfXUL2emPh0ROJezvraLO3LdcAZYWl9Si6vvxL-qU.woff) format('woff'); | |
24 | } | |
25 | @font-face { | |
26 | font-family: 'Ubuntu'; | |
27 | font-style: italic; | |
28 | font-weight: 400; | |
29 | src: local('Ubuntu Italic'), local('Ubuntu-Italic'), url(/fonts/ubuntu/kbP_6ONYVgE-bLa9ZRbvvnYhjbSpvc47ee6xR_80Hnw.woff) format('woff'); | |
30 | } | |
31 | 7 | html#no-overflow{overflow-x:hidden;} |
32 | 8 | body { |
33 | font:62.5%/1.5 'Ubuntu',Helvetica,Arial,sans-serif!important; | |
9 | font:62.5%/1.5 ,Helvetica,Arial,sans-serif!important; | |
34 | 10 | color: #2D2F31; |
35 | background: #E8EFF0!important; | |
11 | background: #F5F5F5 !important; | |
36 | 12 | } |
37 | 13 | ul{padding:0 0 0 0;margin-bottom: 20px;} |
38 | 14 | a { |
175 | 151 | text-align: left; |
176 | 152 | } |
177 | 153 | .seccion article header { |
178 | background: #3F4649; | |
154 | background: #FFFFFF; | |
179 | 155 | min-height: 40px; |
180 | 156 | text-align: left; |
181 | 157 | } |
182 | 158 | .seccion article header h2 { |
183 | color: #fff; | |
184 | font-size: 17px; | |
159 | color: #101010; | |
160 | font-size: 14px; | |
185 | 161 | width: 100%; |
186 | 162 | margin: 0 12px; |
187 | 163 | padding: 0; |
210 | 186 | } |
211 | 187 | .sin_padding h2{padding-bottom: 16px;} |
212 | 188 | |
213 | /* ==== COLORES generales ==== */ | |
214 | /* Fondos */ | |
189 | /* ==== General COLORS ==== */ | |
190 | /* Backgrounds Legacy */ | |
215 | 191 | .fondo-rojo, .fondo-high, .fondo-error {background-color: #DF3936 !important;} |
216 | 192 | .fondo-negro {background-color: #000 !important;} |
217 | 193 | .fondo-gris1, .fondo-critical {background-color: #932ebe !important;} |
221 | 197 | .fondo-naranja, .fondo-med {background-color: #DFBF35 !important;} |
222 | 198 | .fondo-amarillo, .fondo-low, .fondo-created{background-color: #A1CE31 !important;} |
223 | 199 | .fondo-violeta {background-color: #932ebe !important;} |
224 | /* Textos*/ | |
200 | /* Backgrounds New */ | |
201 | .background-unclassified { | |
202 | background-color: #ffffff !important; | |
203 | border: solid 1px #999999; | |
204 | } | |
205 | .background-info { | |
206 | background-color: #ffffff !important; | |
207 | border: solid 1px #2e97bd; | |
208 | } | |
209 | .background-low { | |
210 | background-color: #ffffff !important; | |
211 | border: solid 1px #a1ce31; | |
212 | } | |
213 | .background-med { | |
214 | background-color: #ffffff !important; | |
215 | border: solid 1px #dfbf35; | |
216 | } | |
217 | .background-high { | |
218 | background-color: #ffffff !important; | |
219 | border: solid 1px #df3936; | |
220 | } | |
221 | .background-critical { | |
222 | background-color: #ffffff !important; | |
223 | border: solid 1px #932ebe; | |
224 | } | |
225 | /* Texts Legacy*/ | |
225 | 226 | .texto-rojo {color: #DF3936;} |
226 | 227 | .texto-negro {color: #000;} |
227 | 228 | .texto-gris1 {color: #2D2F31;} |
228 | .texto-blanco {color: #fff;} | |
229 | ||
230 | /* Home Menu grande */ | |
229 | .texto-blanco {color: #6B6565;} | |
230 | /* Texts New*/ | |
231 | .text-unclassified { | |
232 | color: #999999 !important; | |
233 | } | |
234 | .text-info { | |
235 | color: #2e97bd !important; | |
236 | } | |
237 | .text-low { | |
238 | color: #a1ce31 !important; | |
239 | } | |
240 | .text-med { | |
241 | color: #dfbf35 !important; | |
242 | } | |
243 | .text-high { | |
244 | color: #df3936 !important; | |
245 | } | |
246 | .text-critical { | |
247 | color: #932ebe !important; | |
248 | } | |
249 | /* Opacity */ | |
250 | .opacity-low { | |
251 | opacity: 0.6; | |
252 | } | |
253 | .opacity-med { | |
254 | opacity: 0.8; | |
255 | } | |
256 | .opacity-high { | |
257 | opacity: 1; | |
258 | } | |
259 | /* Big Home menu */ | |
231 | 260 | .home-list { |
232 | 261 | margin-top: 20px; |
233 | 262 | } |
305 | 334 | |
306 | 335 | /* Icons on Home */ |
307 | 336 | .icons-color-home{color: #B3B4B5;} |
308 | .fa.icons-size-home{font-size: 9em;} | |
337 | .fa.host{font-size: 2.6em;} | |
309 | 338 | |
310 | 339 | /* Datos con nro grande, titulo y texto */ |
311 | 340 | .dato1 { |
321 | 350 | .dato2 * { |
322 | 351 | text-align: center; |
323 | 352 | } |
353 | .dato2:hover { | |
354 | ||
355 | } | |
324 | 356 | .dato2 .nro { |
325 | 357 | font-size: 300%; |
326 | font-weight: 300; | |
358 | font-weight: 400; | |
327 | 359 | letter-spacing: -.1em; |
328 | 360 | } |
329 | 361 | .dato2 .txt { |
348 | 380 | margin: 5px; |
349 | 381 | } |
350 | 382 | table thead th a { |
351 | color: #bbb; | |
383 | color: #2E91D8; | |
352 | 384 | font-weight: normal; |
353 | 385 | } |
354 | 386 | table thead th a:hover { |
377 | 409 | cursor: pointer; |
378 | 410 | } |
379 | 411 | table.tablesorter thead tr th, table.tablesorter tfoot tr th { |
380 | background-color: #3F464C; | |
412 | background-color: #FFFFFF; | |
381 | 413 | color: #fff; |
382 | 414 | border: 1px solid #FFF; |
383 | font-size: 14px; | |
415 | font-size: 12px; | |
384 | 416 | } |
385 | 417 | |
386 | 418 | table.tablesorter tbody td { |
456 | 488 | table.status-report tbody td { |
457 | 489 | color: #3D3D3D; |
458 | 490 | padding: 5px; |
459 | vertical-align: top; | |
460 | 491 | } |
461 | 492 | |
462 | 493 | table.status-report tbody tr.odd td { |
484 | 515 | background-color: #f2dede !important; |
485 | 516 | } |
486 | 517 | |
518 | table.last-vuln th{text-align: center;} | |
519 | #list .tablesorter thead th:first-child{width: 49%;} | |
487 | 520 | #list .tablesorter thead th { |
488 | width: 45%; | |
521 | width: 36%; | |
522 | text-align: center; | |
489 | 523 | border:1px solid; |
490 | 524 | } |
491 | 525 | #list tbody{display: block} |
493 | 527 | #list .tablesorter tbody td {width: 51.6%} |
494 | 528 | #list .tablesorter tbody td:nth-child(2){width: 51%;word-break: break-all;} |
495 | 529 | #list .tablesorter thead th:last-child{width: 19%} |
530 | /* Fixed */ | |
531 | .table-fixed thead { | |
532 | width: 100%; | |
533 | } | |
534 | .table-fixed tbody { | |
535 | height: 230px; | |
536 | overflow-y: auto; | |
537 | width: 100%; | |
538 | } | |
539 | .table-fixed thead, .table-fixed tbody, .table-fixed tr, .table-fixed td, .table-fixed th { | |
540 | display: block; | |
541 | } | |
542 | .table-fixed tbody td, .table-fixed thead > tr> th { | |
543 | float: left; | |
544 | border-bottom-width: 0; | |
545 | } | |
546 | .table-fixed th{text-align: center} | |
547 | .table-fixed th{padding-right: 6%!important;} | |
496 | 548 | #compound tbody{display: block;max-height: 370px} |
497 | #compound .tablesorter tr {display: table-row;} | |
498 | #compound .tablesorter tbody td {width: 48%} | |
499 | 549 | #compound .tablesorter tbody td span,#compound .tablesorter tbody td img{margin-right: 25px} |
500 | #compound .tablesorter thead th:first-child{width: 54%} | |
501 | #compound .tablesorter thead th { | |
502 | width: 27%; | |
503 | float: left; | |
504 | border:1px solid; | |
505 | } | |
506 | #compound .tablesorter thead th:last-child{width: 19%} | |
507 | 550 | article#compound.panel{ |
508 | 551 | height: 93px; |
509 | 552 | padding-bottom: 428px; |
611 | 654 | #treemap, #bar, #cake{height:195px} |
612 | 655 | #vulns .columna{margin-left:2%} |
613 | 656 | #vulns .main{width:100%;height:105px} |
614 | #vulns-by-price .main{width:100% !important;height: 180px !important;} | |
657 | #vulns-by-price .main{width:100% !important;height: 140px !important;} | |
615 | 658 | #vulns .columna.unsexto{margin: auto;} |
616 | 659 | #summarized .main{height:197px} |
617 | 660 | #summarized .panel.panel-default,#byservices .panel.panel-default{max-height: 239px} |
677 | 720 | .left_auths{margin:5px;float:right;margin-top: -52px;} |
678 | 721 | .left_auth{margin:5px;float:right;margin-top: -40px;} |
679 | 722 | |
680 | div.box div.columna:nth-child(even){background-color: #DF3936; color: #000!important;} | |
681 | div.box div.columna:nth-child(odd){background-color: #000; color: #DF3936!important;} | |
723 | div.box div.columna { | |
724 | background-color: #F5F5F5; color: #6B6565!important; | |
725 | } | |
726 | div.box div.columna:hover { | |
727 | background-color: #E9E9E9; color: #6B6565!important; | |
728 | } | |
682 | 729 | #merge, #delete, #new{margin:5px;float:right;} |
683 | 730 | .input-sm{float: left;margin-bottom: 20px;} |
684 | 731 | /* Media */ |
936 | 983 | font-weight: bold; |
937 | 984 | } |
938 | 985 | .text-center{text-align: center; width: 25px;} |
986 | .severities .color-unclassified, .severities .color-unclassified:hover{color: #999;} | |
987 | .severities .color-info, .severities .color-info:hover{color: #2e97bd;} | |
988 | .severities .color-low, .severities .color-low:hover{color: #A1CE31;} | |
989 | .severities .color-med, .severities .color-med:hover{color: #DFBF35;} | |
990 | .severities .color-high, .severities .color-high:hover{color: #DF3936;} | |
991 | .severities .color-critical, .severities .color-critical:hover{color: #932ebe;} | |
992 | .severities > button{ | |
993 | width: 100%; | |
994 | text-align: left; | |
995 | } | |
996 | .severities > button > span{ | |
997 | color: #000; | |
998 | float: right; | |
999 | margin-top: 8px; | |
1000 | } | |
1001 | .severities > .dropdown-menu-left{left: 14px;} | |
1002 | .severities button.button-error{ | |
1003 | border-color: #a94442; | |
1004 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); | |
1005 | box-shadow: inset 0 1px 1px rgba(0,0,0,.075); | |
1006 | } |
Binary diff not shown
234 | 234 | || v.os.toLowerCase().indexOf("unix") > -1) icon = "linux"; |
235 | 235 | var os = ""; |
236 | 236 | if(icon === "") { |
237 | os = "<span class=\"glyphicon glyphicon-question-sign faraday-qtips\" title="+v.os+"></span>"; | |
237 | os = "<span class=\"fa fa-laptop faraday-qtips\" title="+v.os+"></span>"; | |
238 | 238 | } else { |
239 | 239 | os = "<img src=\"../././reports/images/"+icon+".png\" class=\"faraday-qtips\" title=\""+v.os+"\"/>"; |
240 | 240 | } |
Binary diff not shown
210 | 210 | || v.os.toLowerCase().indexOf("unix") > -1) icon = "linux"; |
211 | 211 | var os = ""; |
212 | 212 | if(icon === "") { |
213 | os = "<span class=\"glyphicon glyphicon-question-sign faraday-qtips\" title="+v.os+"></span>"; | |
213 | os = "<span class=\"fa fa-laptop faraday-qtips\" title="+v.os+"></span>"; | |
214 | 214 | } else { |
215 | 215 | os = "<img src=\"../././reports/images/"+icon+".png\" class=\"faraday-qtips\" title=\""+v.os+"\"/>"; |
216 | 216 | } |
261 | 261 | || cleanOs.toLowerCase().indexOf("unix") > -1) icon = "linux"; |
262 | 262 | var os = ""; |
263 | 263 | if(icon === "") { |
264 | os = "<span class=\"glyphicon glyphicon-question-sign faraday-qtips\" title="+cleanOs+"></span>"; | |
264 | os = "<span class=\"fa fa-laptop faraday-qtips\" title="+cleanOs+"></span>"; | |
265 | 265 | } else { |
266 | 266 | os = "<img src=\"../././reports/images/"+icon+".png\" class=\"faraday-qtips\" title=\""+cleanOs+"\"/>"; |
267 | 267 | } |
24 | 24 | })()) |
25 | 25 | .constant("SEVERITIES", (function() { |
26 | 26 | var severities = [ |
27 | "unclassified", | |
27 | "critical", | |
28 | "high", | |
29 | "med", | |
30 | "low", | |
28 | 31 | "info", |
29 | "low", | |
30 | "med", | |
31 | "high", | |
32 | "critical" | |
32 | "unclassified" | |
33 | 33 | ]; |
34 | 34 | return severities; |
35 | 35 | })()); |
36 | 36 | |
37 | faradayApp.config(['$routeProvider', function($routeProvider) { | |
37 | faradayApp.config(['$routeProvider', 'ngClipProvider', function($routeProvider, ngClipProvider) { | |
38 | ngClipProvider.setPath("script/ZeroClipboard.swf"); | |
38 | 39 | $routeProvider. |
39 | 40 | when('/dashboard/ws/:wsId', { |
40 | 41 | templateUrl: 'scripts/dashboard/partials/dashboard.html', |
5 | 5 | <h5>{{msg}}</h5> |
6 | 6 | </div> |
7 | 7 | <div class="modal-body"> |
8 | <select class="form-control" ng-model="data.property" ng-options="o as o for o in options"> | |
9 | </select> | |
8 | <div class="form-group"> | |
9 | <div class="col-md-12 form-group severities"> | |
10 | <button type="button" class="btn btn-default dropdown-toggle color-{{data.property}}" data-toggle="dropdown" title="Change severity"> | |
11 | {{data.property || 'Edit severity'}} <span class="caret"></span> | |
12 | </button> | |
13 | <ul id="nav" class="dropdown-menu dropdown-menu-left" role="menu"> | |
14 | <li ng-repeat="o in options"><a href="" class="ws color-{{o}}" ng-click="data.property = o">{{o}}</a></li> | |
15 | </ul><!-- WS navigation --> | |
16 | </div> | |
17 | </div> | |
10 | 18 | </div> |
11 | 19 | <div class="modal-footer"> |
12 | 20 | <button class="btn btn-danger" ng-click="cancel()">Cancel</button> |
35 | 35 | </small> |
36 | 36 | </a> |
37 | 37 | <a href="#/hosts" class="ws-link item animated flipInX"> |
38 | <i class="fa fa-sitemap fa-5x icons-color-home icons-size-home"></i> | |
38 | <i class="fa fa-sitemap fa-4x icons-color-home"></i> | |
39 | 39 | <span class="ws-name">Hosts</span> |
40 | 40 | <small> |
41 | 41 | Hosts and Services CRUD<br/> |
2 | 2 | <!-- See the file 'doc/LICENSE' for the license information --> |
3 | 3 | |
4 | 4 | <div class="modal-header"> |
5 | <h3 class="modal-title">Oops!</h3> | |
5 | <h3 class="modal-title"><span class="glyphicon glyphicon-exclamation-sign"></span>Oops!</h3> | |
6 | 6 | </div> |
7 | 7 | <div class="modal-body"> |
8 | 8 | <h5>{{ msg }}</h5> |
67 | 67 | .data(data) |
68 | 68 | .enter() |
69 | 69 | .append('rect') |
70 | .attr("class", "bar") | |
70 | .attr("class", function(d) { return "id-" + d.key + " bar"; }) | |
71 | 71 | .attr("x", function(d) { return x(d.key); }) |
72 | 72 | .attr("y", function(d) { return y(d.value - 0.5); }) |
73 | 73 | .style("fill", function(d) { return color(Math.random()*55); }) |
74 | 74 | .attr("height", function(d) { return height - margin.top - margin.bottom - y(d.value); }) |
75 | 75 | .attr("width", 30) |
76 | 76 | .style('opacity', 0) |
77 | .on('mouseover', function(d){ | |
78 | workspace = $routeParams.wsId; | |
79 | var hurl = "/" + workspace + "/_design/hosts/_view/hosts"; | |
80 | hosts = get_obj(hurl); | |
81 | var name = hosts[d.key].name; | |
82 | document.getElementById("barText").innerHTML = "<div style='background-color:" + d.color + "'><b>" + name + '</b></div>' + d.value; | |
83 | ||
77 | .on('mouseover', function(d) { | |
78 | workspace = $routeParams.wsId; | |
79 | var hurl = "/" + workspace + "/_design/hosts/_view/hosts"; | |
80 | hosts = get_obj(hurl); | |
81 | var name = hosts[d.key].name; | |
82 | document.getElementById("barText").innerHTML = "<div style='background-color:" + d.color + "'><b>" + name + '</b></div>' + d.value; | |
84 | 83 | }) |
85 | .on('mouseleave', function(){ | |
86 | document.getElementById("barText").innerHTML = ""; | |
84 | .on('mouseenter', function(d) { | |
85 | var line = d3.select('.id-'+d.key) | |
86 | .style("opacity", 1); | |
87 | }) | |
88 | .on('mouseleave', function(d) { | |
89 | document.getElementById("barText").innerHTML = ""; | |
90 | var line = d3.select('.id-'+d.key) | |
91 | .style("opacity", 0.8); | |
87 | 92 | }) |
88 | 93 | .transition() |
89 | 94 | .duration(1250) |
90 | .style('opacity', 1); | |
95 | .style('opacity', 0.8); | |
91 | 96 | |
92 | 97 | function get_obj(ourl) { |
93 | 98 | var ls = {}; |
88 | 88 | .attr("display", function(d) { return d.depth ? null : "none"; }) |
89 | 89 | .attr("d", arc) |
90 | 90 | .attr("fill-rule", "evenodd") |
91 | .attr("class", function(d) { | |
92 | var key = ""; | |
93 | if(d.key) { | |
94 | key = "cake-" + d.key; | |
95 | } | |
96 | return key; | |
97 | }) | |
91 | 98 | .style("fill", function(d) {return d.color; }) |
92 | 99 | .style("stroke-width", "0.5") |
93 | 100 | .style("opacity", 0) |
94 | .on('mouseover', function(d){ | |
95 | document.getElementById("cakeText").innerHTML = "<div style='background-color:" + d.color + "'><b>" + d.key + '</b></div>' + d.value; | |
101 | .on('mouseover', function(d) { | |
102 | document.getElementById("cakeText").innerHTML = "<div style='background-color:" + d.color + "'><b>" + d.key + '</b></div>' + d.value; | |
96 | 103 | }) |
97 | .on('mouseleave', function(){ | |
98 | document.getElementById("cakeText").innerHTML = ""; | |
104 | .on('mouseenter', function(d) { | |
105 | var slice = d3.select('.cake-'+d.key) | |
106 | .style("opacity", 1); | |
107 | }) | |
108 | .on('mouseleave', function(d) { | |
109 | var slice = d3.select('.cake-'+d.key) | |
110 | .style("opacity", 0.8); | |
111 | document.getElementById("cakeText").innerHTML = ""; | |
99 | 112 | }) |
100 | 113 | .transition() |
101 | 114 | .duration(1250) |
102 | .style('opacity', 1); | |
115 | .style('opacity', 0.8); | |
103 | 116 | |
104 | 117 | // Get total size of the tree = value of root node from partition. |
105 | 118 | totalSize = path.node().__data__.value; |
27 | 27 | d3.select('.stackedbars').selectAll('svg').remove(); |
28 | 28 | |
29 | 29 | var margins = { |
30 | top: 12, | |
31 | left: 24, | |
32 | right: 12, | |
33 | bottom: 12 | |
30 | top: 5, | |
31 | left: 15, | |
32 | right: 5, | |
33 | bottom: 5 | |
34 | 34 | }; |
35 | 35 | |
36 | 36 | // get parent width to calculate graphic width |
37 | 37 | var pwidth = ele.parent().width(), |
38 | 38 | width = pwidth * 0.9, |
39 | height = 80 - margins.top - margins.bottom; | |
39 | height = 30 - margins.top - margins.bottom; | |
40 | 40 | |
41 | 41 | var dataset = data.map(function(d) { |
42 | 42 | return [{ |
116 | 116 | .attr('tooltip-append-to-body', true) |
117 | 117 | .attr('tooltip', function(d) { |
118 | 118 | return d.k + " sums $" + d.x; |
119 | }) | |
120 | .attr('class', function(d) { | |
121 | return "sb-" + d.k; | |
122 | }) | |
123 | .style('opacity', 0.8) | |
124 | .on('mouseenter', function(d) { | |
125 | var line = d3.select('.sb-'+d.k) | |
126 | .style("opacity", 1); | |
127 | }) | |
128 | .on('mouseleave', function(d) { | |
129 | var line = d3.select('.sb-'+d.k) | |
130 | .style("opacity", 0.8); | |
119 | 131 | }); |
120 | 132 | |
121 | 133 | ele.removeAttr("d3-horizontal-stacked-bar"); |
61 | 61 | var node = div.datum(data_cp).selectAll(".node") |
62 | 62 | .data(treemap.nodes) |
63 | 63 | .enter().append("div") |
64 | .attr("class", "node treemap-tooltip") | |
64 | .attr("class", function(d) { | |
65 | var ret = "node treemap-tooltip"; | |
66 | if(d.key) ret += " tm-" + d.key; | |
67 | return ret; | |
68 | }) | |
65 | 69 | .call(position) |
66 | 70 | .style("background", function(d) { return d.color; }) |
67 | 71 | .style('opacity', 0) |
78 | 82 | document.getElementById("treemapTextModel").innerHTML = "<div style='background-color:" + d.color + "'>" + d.key + '</div>' + d.value; |
79 | 83 | } |
80 | 84 | }) |
81 | .on('mouseleave', function(){ | |
85 | .on('mouseenter', function(d) { | |
86 | var line = d3.select('.tm-'+d.key) | |
87 | .style("opacity", 1); | |
88 | }) | |
89 | .on('mouseleave', function(d) { | |
90 | var line = d3.select('.tm-'+d.key) | |
91 | .style("opacity", 0.8); | |
82 | 92 | document.getElementById("treemapText").innerHTML = ""; |
83 | 93 | }) |
84 | 94 | .transition() |
85 | 95 | .duration(1250) |
86 | .style('opacity', 1); | |
96 | .style('opacity', 0.8); | |
87 | 97 | |
88 | 98 | }; |
89 | 99 | }); |
260 | 260 | } |
261 | 261 | }; |
262 | 262 | |
263 | $scope.showServices = function(host_id) { | |
263 | $scope.showServices = function(host) { | |
264 | 264 | if ($scope.workspace != undefined){ |
265 | 265 | var modal = $modal.open({ |
266 | 266 | templateUrl: 'scripts/dashboard/partials/modal-services-by-host.html', |
267 | 267 | controller: 'summarizedCtrlServicesModal', |
268 | 268 | size: 'lg', |
269 | 269 | resolve: { |
270 | host_id: function(){ | |
271 | return host_id | |
270 | host: function() { | |
271 | return host | |
272 | 272 | }, |
273 | workspace: function(){ | |
273 | workspace: function() { | |
274 | 274 | return $scope.workspace; |
275 | 275 | } |
276 | 276 | } |
355 | 355 | |
356 | 356 | angular.module('faradayApp') |
357 | 357 | .controller('summarizedCtrlServicesModal', |
358 | ['$scope', '$modalInstance', 'dashboardSrv', 'workspace', 'host_id', | |
359 | function($scope, $modalInstance, dashboardSrv, workspace, host_id) { | |
360 | ||
358 | ['$scope', '$modalInstance', 'dashboardSrv', 'workspace', 'host', | |
359 | function($scope, $modalInstance, dashboardSrv, workspace, host) { | |
360 | ||
361 | $scope.host = host | |
361 | 362 | $scope.sortField = 'port'; |
362 | 363 | $scope.sortReverse = false; |
363 | 364 | |
377 | 378 | $scope.sortReverse = !$scope.sortReverse; |
378 | 379 | } |
379 | 380 | |
380 | dashboardSrv.getServicesByHost(workspace, host_id).then(function(services){ | |
381 | dashboardSrv.getName(workspace, host_id).then(function(name){ | |
381 | dashboardSrv.getServicesByHost(workspace, host._id).then(function(services){ | |
382 | dashboardSrv.getName(workspace, host._id).then(function(name){ | |
382 | 383 | $scope.name = name; |
383 | 384 | $scope.services = services; |
384 | 385 | }) |
0 | <article id='compound' class='panel panel-default'> | |
0 | <article id="compound" class='panel panel-default'> | |
1 | 1 | <header> |
2 | 2 | <h2><a href="#/hosts/ws/{{workspace}}">Hosts</a> |
3 | 3 | <span class="glyphicon glyphicon-info-sign" tooltip="All hosts, each one showing its service count and operating system. By clicking on a host IP you can access a list with all of its services"></span> |
10 | 10 | </button> |
11 | 11 | <p>No hosts found yet.</p> |
12 | 12 | </div> |
13 | <table id="hosts" ng-if="hosts.length > 0" class="tablesorter table table-striped"> | |
13 | <table id="hosts" ng-if="hosts.length > 0" class="tablesorter table table-striped table-fixed"> | |
14 | 14 | <thead> |
15 | 15 | <tr> |
16 | <th><a href="" ng-click="hostToggleSort('name')">Host</a></th> | |
17 | <th><a href="" ng-click="hostToggleSort('servicesCount')">Services</a></th> | |
18 | <th><a href="" ng-click="hostToggleSort('os')">OS</a></th> | |
16 | <th class="col-xs-6"><a href="" ng-click="hostToggleSort('name')">Host</a></th> | |
17 | <th class="col-xs-4"><a href="" ng-click="hostToggleSort('servicesCount')">Services</a></th> | |
18 | <th class="col-xs-2"><a href="" ng-click="hostToggleSort('os')">OS</a></th> | |
19 | 19 | </tr> |
20 | 20 | </thead> |
21 | 21 | <tbody> |
22 | 22 | <tr ng-repeat="host in hosts | orderBy:hostSortField:hostSortReverse | |
23 | 23 | startFrom:currentPage*pageSize | limitTo:pageSize"> |
24 | <td><a href="" class="host" ng-click="showServices(host.id)">{{host.name}}</a></td> | |
25 | <td>{{host.servicesCount}}</td> | |
26 | <td> | |
24 | <td class="col-xs-6"> | |
25 | <a href="" class="host" ng-click="showServices(host)">{{host.name}}</a> | |
26 | <a href="//www.shodan.io/search?query={{host.name}}" tooltip="Search in Shodan" target="_blank"> | |
27 | <img ng-src="../././reports/images/shodan.png" height="15px" width="15px" /> | |
28 | </a> | |
29 | </td> | |
30 | <td class="col-xs-6">{{host.servicesCount}}</td> | |
31 | <td class="col-xs-4"> | |
27 | 32 | <img ng-if="host.icon != undefined" ng-src="../././reports/images/{{host.icon}}.png" tooltip="{{host.os}}"/> |
28 | <span ng-if="host.icon == undefined" class="glyphicon glyphicon-question-sign" tooltip="{{host.os}}"></span> | |
33 | <span ng-if="host.icon == undefined" class="fa fa-laptop" tooltip="{{host.os}}"></span> | |
29 | 34 | </td> |
30 | 35 | </tr> |
31 | 36 | </tbody> |
44 | 49 | </form> |
45 | 50 | </div> |
46 | 51 | </div> |
47 | </article>⏎ | |
52 | </article> |
5 | 5 | <div class="right-main"> |
6 | 6 | <div id="reports-main" class="fila clearfix"> |
7 | 7 | <h2 class="ws-label"> |
8 | <span id="ws-name" class="label label-default" | |
9 | title="Current workspace">Dashboard for {{ workspace }}</span><!-- WS name --> | |
8 | <span id="ws-name" title="Current workspace">Dashboard for {{ workspace }}</span><!-- WS name --> | |
10 | 9 | <div id="ws-control" class="btn-group"> |
11 | 10 | <button id="refresh" type="button" class="btn btn-danger" title="Refresh current workspace" ng-click="location.reload()"> |
12 | 11 | <span class="glyphicon glyphicon-refresh"></span> |
24 | 24 | <tbody> |
25 | 25 | <tr ng-repeat="vuln in vulns | orderObjectBy:vulnSortField:vulnSortReverse | limitTo:5"> |
26 | 26 | <td>{{vuln.metadata.create_time * 1000 | date:"MM/dd/yyyy 'at' h:mma"}}</td> |
27 | <td>{{vuln.target}}</td> | |
27 | <td> | |
28 | {{vuln.target}} | |
29 | <a href="//www.shodan.io/search?query={{vuln.target}}" tooltip="Search in Shodan" target="_blank"> | |
30 | <img ng-src="../././reports/images/shodan.png" height="15px" width="15px" /> | |
31 | </a> | |
32 | </td> | |
28 | 33 | <td>{{vuln.severity}}</td> |
29 | 34 | <td class="wrapword">{{vuln.name}}</td> |
30 | 35 | <td> |
17 | 17 | <tbody> |
18 | 18 | <tr ng-repeat="host in hosts | orderBy:sortField:sortReverse"> |
19 | 19 | <td><input disabled type="checkbox" ng-model="host.owned"/></td> |
20 | <td>{{host.name}}</td> | |
20 | <td> | |
21 | {{host.name}} | |
22 | <a href="//www.shodan.io/search?query={{host.name}}" tooltip="Search in Shodan" target="_blank"> | |
23 | <img ng-src="../././reports/images/shodan.png" height="15px" width="15px" /> | |
24 | </a> | |
25 | </td> | |
21 | 26 | <td>{{host.os}}</td> |
22 | 27 | </tr> |
23 | 28 | </tbody> |
29 | 34 | |
30 | 35 | <div class="modal-footer"> |
31 | 36 | <button class="btn btn-success" ng-click="ok()">OK</button> |
32 | </div>⏎ | |
37 | </div> |
18 | 18 | </thead> |
19 | 19 | <tbody> |
20 | 20 | <tr ng-repeat="srv in services | orderBy:sortField:sortReverse"> |
21 | <td>{{srv.name}}</a></td> | |
21 | <td> | |
22 | {{srv.name}} | |
23 | <a href="//www.shodan.io/search?query={{srv.name}}" tooltip="Search in Shodan" target="_blank"> | |
24 | <img ng-src="../././reports/images/shodan.png" height="15px" width="15px" /> | |
25 | </a> | |
26 | </td> | |
22 | 27 | <td>{{srv.description}}</td> |
23 | <td>{{srv.port}}</td> | |
28 | <td> | |
29 | <li ng-repeat="p in srv.ports"> | |
30 | {{p}} | |
31 | <a href="//www.shodan.io/search?query=port:{{p}}" tooltip="Search in Shodan" target="_blank"> | |
32 | <img ng-src="../././reports/images/shodan.png" height="15px" width="15px" /> | |
33 | </a> | |
34 | </li> | |
35 | </td> | |
24 | 36 | <td>{{srv.protocol}}</td> |
25 | 37 | <td>{{srv.status}}</td> |
26 | 38 | </tr> |
27 | 39 | </tbody> |
40 | <div class="col md-12" style="border-top:1px solid #e5e5e5"> | |
41 | <h5><span class="fa fa-laptop"></span> Operating System: <b>{{host.os}}</b></h5> | |
42 | <h6><span class="glyphicon glyphicon-user"></span> Created on the <b>{{host.metadata.create_time * 1000 | date:'MM/dd/yyyy'}} </b></h6> | |
43 | </div><!-- .col md-12 --> | |
28 | 44 | </table> |
29 | 45 | </div> |
30 | 46 | |
31 | 47 | <div class="modal-footer"> |
32 | 48 | <button class="btn btn-success" ng-click="ok()">OK</button> |
33 | </div>⏎ | |
49 | </div> |
52 | 52 | <div class='box'> |
53 | 53 | <div ng-repeat="obj in objectsCount" class="columna unquinto"> |
54 | 54 | <article class="dato2"> |
55 | <section> | |
56 | <div class="nro">{{obj.value}}</div> | |
55 | <div ng-switch on="obj.key"> | |
56 | <div class="nro" ng-switch-when="total vulns"><a href="#/status/ws/{{workspace}}">{{obj.value}}</a></div> | |
57 | <div class="nro" ng-switch-when="hosts"><a href="#/hosts/ws/{{workspace}}">{{obj.value}}</a></div> | |
58 | <div class="nro" ng-switch-when="web vulns"><a href="#/status/ws/{{workspace}}/search/type=VulnerabilityWeb">{{obj.value}}</a></div> | |
59 | <div class="nro" ng-switch-when="vulns"><a href="#/status/ws/{{workspace}}/search/type=!VulnerabilityWeb">{{obj.value}}</a></div> | |
60 | <!--#/status/ws/cscan/search/severity=high --> | |
61 | <div class="nro" ng-switch-default>{{obj.value}}</div> | |
62 | </div> | |
57 | 63 | <div class="txt texto-blanco">{{obj.key}}</div> |
58 | 64 | </section> |
59 | 65 | </article> |
12 | 12 | <p>No vulnerabilities found yet.</p> |
13 | 13 | </div> |
14 | 14 | <div class="main" ng-if="vulnsCount.length > 0"> |
15 | <div class="center-lg-6"><h4><i class="fa fa-money fa-2x"></i> {{workspaceWorth | currency}} total</h4></div> | |
15 | <div class="center-lg-6"><h4><i class="fa fa-money"></i> {{workspaceWorth | currency}} total</h4></div> | |
16 | 16 | <div d3-horizontal-stacked-bar data="vulnsCount" class="stackedbars"></div> |
17 | 17 | <div id="vulns-by-price-reference" class="center-lg-6"> |
18 | 18 | <ul class="label-list"> |
19 | <li ng-repeat="(severity, price) in vulnPrices" tooltip="Click on number to edit price"><span class="label vuln fondo-{{severity}}">{{severity}} $<span contenteditable="true" ng-model="vulnPrices[severity]"></span></span></li> | |
19 | <li ng-repeat="(severity, price) in vulnPrices" tooltip="Click on number to edit price"> | |
20 | <span class="label vuln fondo-{{severity}}"> | |
21 | {{severity}} $ | |
22 | <span contenteditable="true" ng-model="vulnPrices[severity]"></span> | |
23 | </span> | |
24 | </li> | |
20 | 25 | </ul> |
21 | 26 | </div><!-- #vulns-by-price-reference .center-lg-6 --> |
22 | 27 | </div> |
13 | 13 | </div> |
14 | 14 | <div class='main box'> |
15 | 15 | <div ng-repeat="vuln in vulnsCount" class="columna unsexto cursor"> |
16 | <article class="dato2 fondo-{{vuln.key}}" ng-click="navigate('/status/ws/'+workspace+'/search/severity='+vuln.key)"> | |
16 | <article class="dato2 background-{{vuln.key}}" ng-click="navigate('/status/ws/'+workspace+'/search/severity='+vuln.key)"> | |
17 | 17 | <section> |
18 | <div class="nro texto-blanco">{{vuln.value}}</div> | |
19 | <div class="txt texto-blanco">{{vuln.key}}</div> | |
18 | <div class="nro text-{{vuln.key}}">{{vuln.value}}</div> | |
19 | <div class="txt text-{{vuln.key}}">{{vuln.key}}</div> | |
20 | 20 | </section> |
21 | 21 | </article> |
22 | 22 | </div> |
5 | 5 | .controller('hostCtrl', |
6 | 6 | ['$scope', '$filter', '$route', '$routeParams', '$modal', 'hostsManager', 'workspacesFact', 'dashboardSrv', 'servicesManager', |
7 | 7 | function($scope, $filter, $route, $routeParams, $modal, hostsManager, workspacesFact, dashboardSrv, servicesManager) { |
8 | ||
9 | 8 | |
10 | 9 | init = function() { |
11 | 10 | $scope.selectall = false; |
23 | 22 | }); |
24 | 23 | // services by host |
25 | 24 | $scope.services = []; |
26 | dashboardSrv.getServicesByHost($scope.workspace, hostId).then(function(services){ | |
27 | services.forEach(function(service){ | |
28 | servicesManager.getService(service.id, $scope.workspace, true).then(function(s){ | |
29 | $scope.services.push(s); | |
25 | dashboardSrv.getServicesByHost($scope.workspace, hostId) | |
26 | .then(function(services) { | |
27 | services.forEach(function(service) { | |
28 | servicesManager.getService(service.id, $scope.workspace, true) | |
29 | .then(function(s) { | |
30 | $scope.services.push(s); | |
31 | }); | |
30 | 32 | }); |
31 | 33 | }); |
32 | }); | |
34 | ||
35 | hostsManager.getAllVulnsCount($scope.workspace) | |
36 | .then(function(vulns) { | |
37 | $scope.vulnsCount = {}; | |
38 | vulns.forEach(function(vuln) { | |
39 | $scope.vulnsCount[vuln.key] = vuln.value; | |
40 | }); | |
41 | }) | |
42 | .catch(function(e) { | |
43 | console.log(e); | |
44 | }); | |
33 | 45 | }; |
34 | 46 | |
35 | 47 | $scope.new = function() { |
112 | 124 | size: 'sm', |
113 | 125 | resolve: { |
114 | 126 | msg: function() { |
115 | return 'No hosts were selected to edit'; | |
127 | return 'No services were selected to edit'; | |
116 | 128 | } |
117 | 129 | } |
118 | 130 | }); |
3 | 3 | |
4 | 4 | angular.module('faradayApp') |
5 | 5 | .controller('hostsCtrl', |
6 | ['$scope', '$filter', '$route', '$routeParams', '$modal', 'hostsManager', 'workspacesFact', | |
7 | function($scope, $filter, $route, $routeParams, $modal, hostsManager, workspacesFact) { | |
6 | ['$scope', '$filter', '$route', '$routeParams', '$modal', 'hostsManager', 'workspacesFact', | |
7 | function($scope, $filter, $route, $routeParams, $modal, hostsManager, workspacesFact) { | |
8 | 8 | |
9 | 9 | init = function() { |
10 | 10 | $scope.selectall = false; |
17 | 17 | $scope.workspaces = wss; |
18 | 18 | }); |
19 | 19 | |
20 | hostsManager.getHosts($scope.workspace).then(function(hosts) { | |
21 | $scope.hosts = hosts; | |
22 | $scope.loadIcons(); | |
23 | }); | |
20 | hostsManager.getHosts($scope.workspace) | |
21 | .then(function(hosts) { | |
22 | $scope.hosts = hosts; | |
23 | $scope.loadIcons(); | |
24 | }); | |
25 | ||
26 | hostsManager.getAllVulnsCount($scope.workspace) | |
27 | .then(function(vulns) { | |
28 | $scope.vulnsCount = {}; | |
29 | vulns.forEach(function(vuln) { | |
30 | var parts = vuln.key.split("."), | |
31 | parent = parts[0]; | |
32 | ||
33 | if(parts.length > 1) $scope.vulnsCount[vuln.key] = vuln.value; | |
34 | if($scope.vulnsCount[parent] == undefined) $scope.vulnsCount[parent] = 0; | |
35 | $scope.vulnsCount[parent] += vuln.value; | |
36 | }); | |
37 | }) | |
38 | .catch(function(e) { | |
39 | console.log(e); | |
40 | }); | |
24 | 41 | }; |
25 | 42 | |
26 | 43 | $scope.loadIcons = function() { |
61 | 78 | |
62 | 79 | $scope.delete = function() { |
63 | 80 | var selected = []; |
64 | ||
65 | for(var i=0; i < $scope.hosts.length; i++) { | |
66 | var host = $scope.hosts[i]; | |
67 | if(host.selected) { | |
68 | selected.push(host._id); | |
69 | } | |
70 | }; | |
81 | $scope.selectedHosts().forEach(function(select) { | |
82 | selected.push(select._id); | |
83 | }); | |
71 | 84 | |
72 | 85 | if(selected.length == 0) { |
73 | 86 | $modal.open(config = { |
148 | 161 | } |
149 | 162 | |
150 | 163 | $scope.edit = function() { |
151 | var selected_host = null; | |
152 | ||
153 | $scope.hosts.forEach(function(host) { | |
154 | if(host.selected) { | |
155 | // if more than one host was selected, | |
156 | // we only use the last one, for now | |
157 | selected_host = host; | |
158 | } | |
159 | }); | |
160 | ||
161 | if(selected_host) { | |
164 | ||
165 | if($scope.selectedHosts().length == 1) { | |
162 | 166 | var modal = $modal.open({ |
163 | 167 | templateUrl: 'scripts/hosts/partials/modalEdit.html', |
164 | 168 | controller: 'hostsModalEdit', |
165 | 169 | size: 'lg', |
166 | 170 | resolve: { |
167 | 171 | host: function(){ |
168 | return selected_host; | |
172 | return $scope.selectedHosts()[0]; | |
169 | 173 | } |
170 | 174 | } |
171 | 175 | }); |
173 | 177 | modal.result.then(function(data) { |
174 | 178 | hostdata = data[0]; |
175 | 179 | interfaceData = data[1]; |
176 | $scope.update(selected_host, hostdata, interfaceData); | |
180 | $scope.update($scope.selectedHosts()[0], hostdata, interfaceData); | |
177 | 181 | }); |
178 | 182 | } else { |
179 | 183 | $modal.open(config = { |
234 | 238 | return interfaceData; |
235 | 239 | }; |
236 | 240 | |
241 | $scope.selectedHosts = function() { | |
242 | selected = []; | |
243 | $scope.hosts.forEach(function(host) { | |
244 | if(host.selected === true) { | |
245 | selected.push(host); | |
246 | } | |
247 | }); | |
248 | return selected; | |
249 | }; | |
250 | ||
237 | 251 | $scope.checkAll = function() { |
238 | 252 | $scope.selectall = !$scope.selectall; |
239 | 253 |
5 | 5 | |
6 | 6 | <div class="right-main"><div id="reports-main" class="fila clearfix"> |
7 | 7 | <h2 class="ws-label"> |
8 | <span id="ws-name" class="label label-default" | |
9 | title="Hosts">Hosts for {{workspace}} ({{hosts.length}})</span><!-- WS name --> | |
8 | <span id="ws-name" title="Hosts">Hosts for {{workspace}} ({{hosts.length}})</span><!-- WS name --> | |
10 | 9 | <div id="ws-control" class="btn-group"> |
11 | 10 | <button id="refresh" type="button" class="btn btn-danger" title="Refresh current workspace" ng-click="location.reload()"> |
12 | 11 | <span class="glyphicon glyphicon-refresh"></span> |
22 | 21 | <span class="glyphicon glyphicon-trash"></span> |
23 | 22 | Delete |
24 | 23 | </button> |
25 | <button id="merge" type="button" class="btn btn-default" title="Edit selected hosts" ng-click="edit()"> | |
24 | <button id="merge" type="button" class="btn btn-default" title="Edit selected hosts" ng-disabled="selectedHosts().length > 1" ng-click="edit()"> | |
26 | 25 | <span class="glyphicon glyphicon-pencil"></span> |
27 | 26 | Edit |
28 | 27 | </button> |
46 | 45 | <a href="" ng-click="toggleSort('description')">Description</a> |
47 | 46 | </th> |
48 | 47 | <th> |
48 | <a href="" ng-click="toggleSort('vulns')">Vulns</a> | |
49 | </th> | |
50 | <th> | |
49 | 51 | <a href="" ng-click="toggleSort('os')">OS</a> |
50 | 52 | </th> |
51 | 53 | <th> |
57 | 59 | <tr ng-repeat="host in hosts | filter:query | orderBy:sortField:reverse" |
58 | 60 | selection-model selection-model-type="checkbox" |
59 | 61 | selection-model-mode="multiple-additive" |
60 | selection-model-selected-class="multi-selected"> | |
62 | selection-model-selected-class="multi-selected" | |
63 | selection-model-on-change="selectedHosts()"> | |
61 | 64 | <td><input type="checkbox" name="{{host._id}}"/></td> |
62 | 65 | <td><a href="#/host/ws/{{workspace}}/hid/{{host._id}}">View</a></td> |
63 | <td ng-bind="host.name"></td> | |
66 | <td> | |
67 | {{host.name}} | |
68 | <a href="//www.shodan.io/search?query={{host.name}}" tooltip="Search in Shodan" target="_blank"> | |
69 | <img ng-src="../././reports/images/shodan.png" height="15px" width="15px" /> | |
70 | </a> | |
71 | </td> | |
64 | 72 | <td ng-bind="host.description || '-'"></td> |
73 | <td> <a ng-href="#/status/ws/{{workspace}}/search/target={{host.name}}" ng-bind="vulnsCount[host._id] || '-'"></a></td> | |
65 | 74 | <td> |
66 | 75 | <img ng-if="host.icon != undefined" ng-src="../././reports/images/{{host.icon}}.png" tooltip="{{host.os}}"/> |
67 | <span ng-if="host.icon == undefined" class="glyphicon glyphicon-question-sign" tooltip="{{host.os}}"></span> | |
76 | <span ng-if="host.icon == undefined" class="fa fa-laptop" tooltip="{{host.os}}"></span> | |
68 | 77 | </td> |
69 | 78 | <td ng-bind="host.owned"></td> |
70 | 79 | </tr> |
11 | 11 | </div> |
12 | 12 | <div class="modal-body"> |
13 | 13 | <div class="form-horizontal"> |
14 | <div class="form-group"> | |
14 | <div class="form-group" ng-class="{'has-error': form.name.$invalid }"> | |
15 | 15 | <div class="col-md-12"> |
16 | 16 | <label class="sr-only" for="name">Name</label> |
17 | <input type="text" class="form-control" id="name" placeholder="Name" ng-model="host.name" required/> | |
17 | <input type="text" class="form-control" id="name" name="name" placeholder="Name" ng-model="host.name" required/> | |
18 | 18 | <span class="help-block normal-size"> |
19 | 19 | Example: 192.168.0.1 |
20 | 20 | </span> |
11 | 11 | </div> |
12 | 12 | <div class="modal-body"> |
13 | 13 | <div class="form-horizontal"> |
14 | <div class="form-group"> | |
14 | <div class="form-group" ng-class="{'has-error': form.name.$invalid }"> | |
15 | 15 | <div class="col-md-12"> |
16 | 16 | <label class="sr-only" for="name">Name</label> |
17 | <input type="text" class="form-control" id="name" placeholder="Name" ng-model="hostdata.name" required/> | |
17 | <input type="text" class="form-control" id="name" name="name" placeholder="Name" ng-model="hostdata.name" required/> | |
18 | 18 | <span class="help-block normal-size"> |
19 | 19 | Example: 192.168.0.1 |
20 | 20 | </span> |
59 | 59 | </div> |
60 | 60 | </div><!-- .form-group --> |
61 | 61 | <div class="form-group"> |
62 | <div class="col-md-6"> | |
62 | <div class="col-md-6" ng-class="{'has-error': form.ipv6.$invalid }"> | |
63 | 63 | <label class="sr-only" for="ipv4">IP v4</label> |
64 | <input type="text" class="form-control" id="ipv4" placeholder="IP v4" ng-model="interfaceData.ipv4" ng-required="!interfaceData.ipv6"/> | |
64 | <input type="text" class="form-control" id="ipv4" name="ipv6" placeholder="IP v4" ng-model="interfaceData.ipv4" ng-required="!interfaceData.ipv6"/> | |
65 | 65 | </div> |
66 | <div class="col-md-6"> | |
66 | <div class="col-md-6" ng-class="{'has-error': form.ipv4.$invalid }"> | |
67 | 67 | <label class="sr-only" for="ipv6">IP v6</label> |
68 | <input type="text" class="form-control" id="ipv6" placeholder="IP v6" ng-model="interfaceData.ipv6" ng-required="!interfaceData.ipv4"/> | |
68 | <input type="text" class="form-control" id="ipv6" name="ipv4" placeholder="IP v6" ng-model="interfaceData.ipv6" ng-required="!interfaceData.ipv4"/> | |
69 | 69 | </div> |
70 | 70 | </div><!-- .form-group --> |
71 | 71 | <div class="form-group"> |
169 | 169 | return deferred.promise; |
170 | 170 | }; |
171 | 171 | |
172 | hostsManager.getAllVulnsCount = function(ws) { | |
173 | var deferred = $q.defer(); | |
174 | ||
175 | var url = BASEURL + ws + '/_design/vulns/_view/byhost?group=true'; | |
176 | ||
177 | $http.get(url) | |
178 | .success(function(vulns) { | |
179 | deferred.resolve(vulns.rows); | |
180 | }) | |
181 | .error(function() { | |
182 | deferred.reject("Unable to load Vulnerabilities"); | |
183 | }); | |
184 | ||
185 | return deferred.promise; | |
186 | }; | |
187 | ||
172 | 188 | hostsManager.getInterfaces = function(ws, id) { |
173 | 189 | var deferred = $q.defer(), |
174 | 190 | self = this; |
64 | 64 | |
65 | 65 | $scope.loadCurrentWorkspace(); |
66 | 66 | |
67 | if(navigator.userAgent.toLowerCase().indexOf('iceweasel') > -1) { | |
68 | $scope.isIceweasel = "Your browser is not supported, please use Firefox or Chrome"; | |
69 | } | |
67 | // if(navigator.userAgent.toLowerCase().indexOf('iceweasel') > -1) { | |
68 | // $scope.isIceweasel = "Your browser is not supported, please use Firefox or Chrome"; | |
69 | // } | |
70 | 70 | |
71 | 71 | }]); |
20 | 20 | </li> |
21 | 21 | <li> |
22 | 22 | <a href="#/hosts/ws/{{workspace}}" class="workspaces" style="color: #ffffff !important" tooltip="Hosts" tooltip-placement="right"> |
23 | <i class="fa fa-sitemap fa-5x"></i> | |
23 | <i class="fa fa-sitemap host"></i> | |
24 | 24 | </a> |
25 | 25 | </li> |
26 | 26 | </ul> |
36 | 36 | var date = new Date(), |
37 | 37 | timestamp = date.getTime()/1000.0; |
38 | 38 | |
39 | $scope.service.ports.forEach(function(port){ | |
40 | ports.push(port.key); | |
41 | }); | |
39 | if($scope.service.ports.length !== 0) { | |
40 | $scope.service.ports.forEach(function(port){ | |
41 | ports.push(port.key); | |
42 | }); | |
43 | $scope.service.ports = ports.filter(Boolean); | |
44 | } else { | |
45 | delete $scope.service.ports; | |
46 | } | |
42 | 47 | |
43 | $scope.service.ports = ports.filter(Boolean); | |
44 | 48 | $modalInstance.close($scope.service); |
45 | 49 | }; |
46 | 50 |
5 | 5 | |
6 | 6 | <div class="right-main"><div id="reports-main" class="fila clearfix"> |
7 | 7 | <h2 class="ws-label"> |
8 | <span id="ws-name" class="label label-default" | |
9 | title="Hosts">Services from {{host.name}}</span><!-- WS name --> | |
8 | <span id="ws-name" title="Hosts">Services from {{host.name}}</span><!-- WS name --> | |
10 | 9 | <div id="ws-control" class="btn-group"> |
11 | 10 | <button id="refresh" type="button" class="btn btn-danger" title="Refresh current workspace" ng-click="location.reload()"> |
12 | 11 | <span class="glyphicon glyphicon-refresh"></span> |
74 | 73 | <th> |
75 | 74 | <a href="" ng-click="toggleSort('status')">Status</a> |
76 | 75 | </th> |
76 | <th> | |
77 | <a href="" ng-click="toggleSort('vulns')">Vulns</a> | |
78 | </th> | |
77 | 79 | </tr> |
78 | 80 | </thead> |
79 | 81 | <tbody> |
82 | 84 | selection-model-mode="multiple-additive" |
83 | 85 | selection-model-selected-class="multi-selected"> |
84 | 86 | <td><input type="checkbox" name="{{s._id}}"/></td> |
85 | <td ng-bind="service.name"></td> | |
87 | <td> | |
88 | {{service.name}} | |
89 | <a href="//www.shodan.io/search?query={{service.name}}" tooltip="Search in Shodan" target="_blank"> | |
90 | <img ng-src="../././reports/images/shodan.png" height="15px" width="15px" /> | |
91 | </a> | |
92 | </td> | |
86 | 93 | <td ng-bind="service.description || '-'"></td> |
87 | <td ng-bind="service.ports"></td> | |
94 | <td> | |
95 | <li ng-repeat="p in service.ports"> | |
96 | {{p}} | |
97 | <a href="//www.shodan.io/search?query=port:{{p}}" tooltip="Search in Shodan" target="_blank"> | |
98 | <img ng-src="../././reports/images/shodan.png" height="15px" width="15px" /> | |
99 | </a> | |
100 | </li> | |
101 | </td> | |
88 | 102 | <td ng-bind="service.protocol"></td> |
89 | 103 | <td ng-bind="service.status"></td> |
104 | <td ng-bind="vulnsCount[service._id] || '-'"></td> | |
90 | 105 | </tr> |
91 | 106 | </tbody> |
92 | 107 | </table><!-- #hosts --> |
11 | 11 | </div> |
12 | 12 | <div class="modal-body"> |
13 | 13 | <div class="form-horizontal"> |
14 | <div class="form-group"> | |
14 | <div class="form-group" ng-class="{'has-error': form.name.$invalid }"> | |
15 | 15 | <div class="col-md-12"> |
16 | 16 | <label class="sr-only" for="name">Name</label> |
17 | <input type="text" class="form-control" id="name" placeholder="Name" ng-model="service.name" required/> | |
17 | <input type="text" class="form-control" id="name" name="name" placeholder="Name" ng-model="service.name" /> | |
18 | 18 | </div> |
19 | 19 | </div><!-- .form-group --> |
20 | 20 | <div class="form-group"> |
11 | 11 | </div> |
12 | 12 | <div class="modal-body"> |
13 | 13 | <div class="form-horizontal"> |
14 | <div class="form-group"> | |
14 | <div class="form-group" ng-class="{'has-error': form.name.$invalid }"> | |
15 | 15 | <div class="col-md-12"> |
16 | 16 | <label class="sr-only" for="service">Name</label> |
17 | <input type="text" class="form-control" id="service" placeholder="Name" ng-model="service.name" required/> | |
17 | <input type="text" class="form-control" id="service" name="name" placeholder="Name" ng-model="service.name" required/> | |
18 | 18 | </div> |
19 | 19 | </div><!-- .form-group --> |
20 | 20 | <div class="form-group"> |
37 | 37 | <div class="col-md-3"> |
38 | 38 | <h5>Ports</h5> |
39 | 39 | <span class="input-group-addon button-radius" ng-click="newPort($event)">Add Port</span> |
40 | <div class="input-margin" ng-repeat="port in service.ports"> | |
40 | <div class="input-margin" ng-repeat="port in service.ports" ng-class="{'has-error': form.port.$invalid }"> | |
41 | 41 | <div class="input-group margin-bottom-sm"> |
42 | 42 | <label class="sr-only" for="port">Ports</label> |
43 | <input type="number" class="form-control" id="port" placeholder="Port" ng-model="port.key" required/> | |
43 | <input type="number" class="form-control" id="port" name="port" placeholder="Port" ng-model="port.key" required/> | |
44 | 44 | <span class="input-group-addon" ng-click="service.ports.splice($index, 1)" ng-hide="service.ports.length == 1"><i class="fa fa-minus-circle"></i></span> |
45 | 45 | </div> |
46 | 46 | </div> |
47 | 47 | </div> |
48 | <div class="col-md-3 protocol"> | |
48 | <div class="col-md-3 protocol" ng-class="{'has-error': form.protocol.$invalid }"> | |
49 | 49 | <h5>Protocol</h5> |
50 | 50 | <label class="sr-only" for="protocol">Protocol</label> |
51 | <input type="text" class="form-control" id="protocol" placeholder="Protocol" ng-model="service.protocol" required/> | |
51 | <input type="text" class="form-control" id="protocol" name="protocol" placeholder="Protocol" ng-model="service.protocol" required/> | |
52 | 52 | </div> |
53 | 53 | <div class="col-md-3"> |
54 | 54 | <h5>Version</h5> |
479 | 479 | } |
480 | 480 | } |
481 | 481 | } |
482 | return encodeURI(encode.slice(1)); | |
482 | return encode.slice(1); | |
483 | 483 | }; |
484 | 484 | |
485 | 485 | // decodes search parameters to object in order to use in filter |
486 | 486 | $scope.decodeSearch = function(search) { |
487 | 487 | var i = -1, |
488 | 488 | decode = {}, |
489 | params = decodeURI(search).split("&"); | |
489 | params = search.split("&"); | |
490 | 490 | |
491 | 491 | params.forEach(function(param) { |
492 | 492 | i = param.indexOf("="); |
4 | 4 | <form name="formEdit" novalidate> |
5 | 5 | <div class="modal-header"> |
6 | 6 | <div class="modal-button"> |
7 | <button class="btn btn-success" ng-click="modal.ok()" ng-disabled="formEdit.$invalid">OK</button> | |
7 | <button class="btn btn-success" ng-click="modal.ok()" ng-disabled="formEdit.$invalid || modal.data.severity === undefined">OK</button> | |
8 | 8 | <button class="btn btn-danger" ng-click="modal.cancel()">Cancel</button> |
9 | 9 | </div> |
10 | 10 | <h3 class="modal-title">Vuln edit</h3> |
23 | 23 | </div> |
24 | 24 | </div> |
25 | 25 | <div class="form-group"> |
26 | <div class="col-md-6"> | |
26 | <div class="col-md-6 severities"> | |
27 | 27 | <h5>Severity</h5> |
28 | <select class="form-control" ng-model="modal.data.severity" ng-options="s as s for s in modal.severities" required> | |
29 | </select> | |
28 | <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}"> | |
29 | {{modal.data.severity || 'Edit severity'}} <span class="caret" style="color:#000"></span> | |
30 | </button> | |
31 | <ul id="nav" class="dropdown-menu dropdown-menu-left" role="menu"> | |
32 | <li ng-repeat="s in modal.severities"><a href="" class="ws color-{{s}}" ng-click="modal.data.severity=s">{{s}}</a></li> | |
33 | </ul><!-- WS navigation --> | |
30 | 34 | </div> |
31 | 35 | <div class="col-md-6"> |
32 | 36 | <h5>Ease of Resolution</h5> |
35 | 39 | </select> |
36 | 40 | </div> |
37 | 41 | </div><!-- .form-group --> |
38 | <div class="form-group"> | |
42 | <div class="form-group" ng-class="{'has-error': formEdit.name.$invalid }"> | |
39 | 43 | <div class="col-md-12"> |
40 | 44 | <label class="sr-only" for="vuln-name">Vuln name</label> |
41 | <input type="text" class="form-control" id="vuln-name" placeholder="Name" ng-model="modal.data.name" required/> | |
45 | <input type="text" class="form-control" id="vuln-name" name="name" placeholder="Name" ng-model="modal.data.name" required/> | |
42 | 46 | </div> |
43 | 47 | </div><!-- .form-group --> |
44 | <div class="form-group"> | |
48 | <div class="form-group" ng-class="{'has-error': formEdit.desc.$invalid }"> | |
45 | 49 | <div class="col-md-12"> |
46 | 50 | <label class="sr-only" for="vuln-desc">Vuln description</label> |
47 | <textarea class="form-control" id="vuln-desc" placeholder="Description" ng-model="modal.data.desc" required></textarea> | |
51 | <textarea class="form-control" id="vuln-desc" name="desc" placeholder="Description" ng-model="modal.data.desc" required></textarea> | |
48 | 52 | </div> |
49 | 53 | </div><!-- .form-group --> |
50 | 54 | <div class="form-group"> |
4 | 4 | <form name="form" novalidate> |
5 | 5 | <div class="modal-header"> |
6 | 6 | <div class="modal-button"> |
7 | <button class="btn btn-success" ng-click="modal.ok()" ng-disabled="form.$invalid">OK</button> | |
7 | <button class="btn btn-success" ng-click="modal.ok()" ng-disabled="form.$invalid || modal.data.severity === undefined">OK</button> | |
8 | 8 | <button class="btn btn-danger" ng-click="modal.cancel()">Cancel</button> |
9 | 9 | </div> |
10 | 10 | <h3 class="modal-title">Vulnerability creation</h3> |
68 | 68 | <select class="form-control" ng-model="modal.data.type" ng-options="option.value as option.name for option in modal.vuln_types"> |
69 | 69 | </select> |
70 | 70 | </div> |
71 | <div class="col-md-4"> | |
71 | <div class="col-md-4 severities"> | |
72 | 72 | <h5>Severity</h5> |
73 | <select class="form-control" ng-model="modal.data.severity" ng-options="s for s in modal.severities" required> | |
74 | </select> | |
73 | <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}"> | |
74 | {{modal.data.severity || 'Add severity'}} <span class="caret"></span> | |
75 | </button> | |
76 | <ul id="nav" class="dropdown-menu dropdown-menu-left" role="menu"> | |
77 | <li ng-repeat="s in modal.severities"><a href="" class="ws color-{{s}}" ng-click="modal.data.severity=s">{{s}}</a></li> | |
78 | </ul><!-- WS navigation --> | |
75 | 79 | </div> |
76 | 80 | <div class="col-md-4"> |
77 | 81 | <h5>Ease of Resolution</h5> |
80 | 84 | </select> |
81 | 85 | </div> |
82 | 86 | </div><!-- .form-group --> |
83 | <div class="form-group"> | |
87 | <div class="form-group" ng-class="{'has-error': form.name.$invalid }"> | |
84 | 88 | <div class="col-md-12"> |
85 | 89 | <label class="sr-only" for="vuln-name">Vuln name</label> |
86 | <input type="text" class="form-control" id="vuln-name" placeholder="Name" ng-model="modal.data.name" required/> | |
87 | </div> | |
88 | </div><!-- .form-group --> | |
89 | <div class="form-group"> | |
90 | <input type="text" class="form-control" id="vuln-name" name="name" placeholder="Name" ng-model="modal.data.name" required/> | |
91 | </div> | |
92 | </div><!-- .form-group --> | |
93 | <div class="form-group" ng-class="{'has-error': form.desc.$invalid }"> | |
90 | 94 | <div class="col-md-12"> |
91 | 95 | <label class="sr-only" for="vuln-desc">Vuln description</label> |
92 | <textarea class="form-control" id="vuln-desc" placeholder="Description" ng-model="modal.data.desc" required></textarea> | |
96 | <textarea class="form-control" id="vuln-desc" name="desc" placeholder="Description" ng-model="modal.data.desc" required></textarea> | |
93 | 97 | </div> |
94 | 98 | </div><!-- .form-group --> |
95 | 99 | <div class="form-group"> |
5 | 5 | <div class="right-main"> |
6 | 6 | <div id="reports-main" class="fila clearfix"> |
7 | 7 | <div class="ws-label"> |
8 | <h2><span id="ws-name" class="label label-default" | |
9 | title="Current workspace">Status report for {{ workspace }} ({{vulns.length}} vulns)</span></h2><!-- WS name --> | |
8 | <h2><span id="ws-name" title="Current workspace">Status report for {{ workspace }} ({{vulns.length}} vulns)</span></h2><!-- WS name --> | |
10 | 9 | </div><!-- .ws-label --> |
11 | 10 | <div id="ws-control" class="btn-group btn-small-margin"> |
12 | 11 | <button file-exporter="csv()" type="button" class="btn btn-success" title="Download CSV for current workspace"> |
199 | 198 | <td class="text-center"><span ng-click="editVuln(v)" class="glyphicon glyphicon-pencil cursor" tooltip="Edit"></span></td> |
200 | 199 | <td class="text-center"><span ng-click="deleteVuln(v)" class="glyphicon glyphicon-trash cursor" tooltip="Delete"></span></td> |
201 | 200 | <td ng-if="columns.date">{{v.metadata.create_time * 1000 | date:'MM/dd/yyyy'}}</td> |
202 | <td ng-if="columns.target">{{v.target}}</td> | |
201 | <td ng-if="columns.target"> | |
202 | <a href="#/status/ws/{{workspace}}/search/target={{v.target}}">{{v.target}}</a> | |
203 | <a href="//www.shodan.io/search?query={{v.target}}" tooltip="Search in Shodan" target="_blank"> | |
204 | <img ng-src="../././reports/images/shodan.png" height="15px" width="15px" /> | |
205 | </a> | |
206 | </td> | |
203 | 207 | <td ng-if="columns.status">Vulnerable</td> |
204 | <td ng-if="columns.severity"><span class="label vuln fondo-{{v.severity}}">{{v.severity}}</span></td> | |
205 | <td ng-if="columns.name">{{v.name}}</td> | |
208 | <td ng-if="columns.severity"><a href="#/status/ws/{{workspace}}/search/severity={{v.severity}}"><span class="label vuln fondo-{{v.severity}}">{{v.severity}}</span></a></td> | |
209 | <td ng-if="columns.name"><a href="#/status/ws/{{workspace}}/search/name={{v.name | encodeURIComponent | encodeURIComponent}}">{{v.name}}</a></td> | |
206 | 210 | <td ng-if="columns.desc" text-collapse text-collapse-max-length="150" text-collapse-text="{{v.desc}}"></td> |
207 | 211 | <td ng-if="columns.data" text-collapse text-collapse-max-length="150" text-collapse-text="{{v.data}}"></td> |
208 | <td ng-if="columns.method">{{v.method}}</td> | |
209 | <td ng-if="columns.path">{{v.path}}</td> | |
210 | <td ng-if="columns.pname">{{v.pname}}</td> | |
212 | <td ng-if="columns.method"><a href="#/status/ws/{{workspace}}/search/method={{v.method}}">{{v.method}}</a></td> | |
213 | <td ng-if="columns.path"><a href="#/status/ws/{{workspace}}/search/path={{v.path}}">{{v.path}}</a></td> | |
214 | <td ng-if="columns.pname"><a href="#/status/ws/{{workspace}}/search/pname={{v.pname}}">{{v.pname}}</a></td> | |
211 | 215 | <td ng-if="columns.params">{{v.params}}</td> |
212 | <td ng-if="columns.query">{{v.query}}</td> | |
216 | <td ng-if="columns.query"><a href="#/status/ws/{{workspace}}/search/query={{v.query}}">{{v.query}}</a></td> | |
213 | 217 | <td ng-if="columns.request" text-collapse text-collapse-max-length="100" text-collapse-text="{{v.request}}"></td> |
214 | 218 | <td ng-if="columns.response" text-collapse text-collapse-max-length="100" text-collapse-text="{{v.response}}"></td> |
215 | 219 | <td ng-if="columns.resolution">{{v.resolution}}</td> |
217 | 221 | <span class="glyphicon glyphicon-ok" ng-show="v.type === 'VulnerabilityWeb'"></span> |
218 | 222 | <span class="glyphicon glyphicon-remove" ng-show="v.type !== 'VulnerabilityWeb'"></span> |
219 | 223 | </td> |
220 | <td ng-if="columns.website">{{v.website}}</td> | |
224 | <td ng-if="columns.website"><a href="#/status/ws/{{workspace}}/search/website={{v.website}}">{{v.website}}</a></td> | |
221 | 225 | <td ng-if="columns.refs"><p ng-repeat="refs in v.refs track by $index">{{refs}}</p></td> |
222 | 226 | <td ng-if="columns.evidence"> |
223 | 227 | <div ng-repeat="(name, file) in v._attachments track by $index"> |
229 | 233 | <p ng-if="rating">{{impact}}</p> |
230 | 234 | </div> |
231 | 235 | </td> |
232 | <td ng-if="columns.easeofresolution">{{v.easeofresolution}}</td> | |
233 | <td ng-if="columns.hostnames"><p ng-repeat="hostname in v.hostnames track by $index">{{hostname}}</p></td> | |
236 | <td ng-if="columns.easeofresolution"><a href="#/status/ws/{{workspace}}/search/easeofresolution={{v.easeofresolution}}">{{v.easeofresolution}}</a></td> | |
237 | <td ng-if="columns.hostnames"> | |
238 | <p ng-repeat="hostname in v.hostnames track by $index"> | |
239 | <a href="#/status/ws/{{workspace}}/search/hostnames={{hostname}}">{{hostname}}</a> | |
240 | <a href="//www.shodan.io/search?query={{hostname}}" tooltip="Search in Shodan" target="_blank"> | |
241 | <img ng-src="../././reports/images/shodan.png" height="15px" width="15px" /> | |
242 | </a> | |
243 | </p> | |
244 | </td> | |
234 | 245 | </tr> |
235 | 246 | </tbody> |
236 | 247 | </table><!-- #hosts --> |
5 | 5 | <section id="main" class="seccion clearfix"> |
6 | 6 | <div class="right-main"><div id="reports-main" class="fila clearfix"> |
7 | 7 | <h2 class="ws-label"> |
8 | <span id="ws-name" class="label label-default" | |
9 | title="Workspaces">Workspaces</span><!-- WS name --> | |
8 | <span id="ws-name" title="Workspaces">Workspaces</span><!-- WS name --> | |
10 | 9 | <button id="delete" type="button" class="btn btn-default" title="Delete selected Workspaces" ng-click="delete()"> |
11 | 10 | <span class="glyphicon glyphicon-trash"></span> |
12 | 11 | Delete |
35 | 34 | <tbody> |
36 | 35 | <tr ng-repeat="ws in workspaces | filter:query | orderBy:sortField:reverse" |
37 | 36 | selection-model selection-model-selected-class="multi-selected"> |
38 | <td><span ng-class-even="'label label-unclassified'" ng-class-odd="'label label-high'">{{ws.name}}</span></td> | |
37 | <td><a href="#/{{hash}}dashboard/ws/{{ws.name}}"><span ng-class-even="'label label-unclassified'" ng-class-odd="'label label-high'">{{ws.name}}</span></td> | |
39 | 38 | <td ng-bind="ws.duration.start || '-' | date:'MM/dd/yyyy'"></td> |
40 | 39 | <td ng-bind="ws.duration.end || '-' | date:'MM/dd/yyyy'"></td> |
41 | <td ng-bind="objects[ws.name]['total vulns']"></td> | |
42 | <td ng-bind="objects[ws.name]['hosts']"></td> | |
40 | <td><a href="#/status/ws/{{ws.name}}">{{objects[ws.name]['total vulns']}}</a></td> | |
41 | <td><a href="#/hosts/ws/{{ws.name}}">{{objects[ws.name]['hosts']}}</a></td> | |
43 | 42 | <td ng-bind="objects[ws.name]['services']"></td> |
44 | 43 | </tr> |
45 | 44 | </tbody> |
16 | 16 | <label class="sr-only" for="vuln-desc">Workspace Description</label> |
17 | 17 | <textarea class="form-control" id="vuln-desc" |
18 | 18 | placeholder="Description" value={{workspace.description}} |
19 | ng-model="workspace.description" required></textarea> | |
19 | ng-model="workspace.description"></textarea> | |
20 | 20 | </div> |
21 | 21 | </div><!-- .form-group --> |
22 | 22 | <div class="form-group"> |
11 | 11 | </div> |
12 | 12 | <div class="modal-body"> |
13 | 13 | <div class="form-horizontal"> |
14 | <div class="form-group"> | |
14 | <div class="form-group" ng-class="{'has-error': form.name.$invalid }"> | |
15 | 15 | <div class="col-md-12"> |
16 | 16 | <ng-form name="form" novalidate> |
17 | 17 | <label class="sr-only" for="wsp-name">Workspace Name</label> |
18 | 18 | <input type="text" class="form-control" |
19 | ng-pattern=/^[a-z][a-z0-9\_\$\(\)\+\-\/]*$/ id="vuln-name" placeholder="Workspace Name" | |
19 | ng-pattern=/^[a-z][a-z0-9\_\$\(\)\+\-\/]*$/ id="vuln-name" name="name" placeholder="Workspace Name" | |
20 | 20 | ng-model="workspace.name" required/> |
21 | 21 | <div ng-if="form.$invalid"> |
22 | 22 | <div class="alert alert-danger target_not_selected" role="alert" ng-hide=""> |
2 | 2 | // See the file 'doc/LICENSE' for the license information |
3 | 3 | function(doc) { |
4 | 4 | if(doc.type == "Vulnerability" || doc.type == "VulnerabilityWeb"){ |
5 | var easeofresolution = "trivial", | |
5 | var easeofresolution = "", | |
6 | 6 | impact = { |
7 | 7 | "accountability": false, |
8 | 8 | "availability": false, |
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 | function(doc) { | |
4 | if(doc.type == "Vulnerability" || doc.type == "VulnerabilityWeb"){ | |
5 | emit(doc.parent, 1); | |
6 | } | |
7 | } |
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 | function (key, values) { | |
6 | ||
7 | return values.reduce(function(previousValue, currentValue, index, array){ | |
8 | return previousValue + currentValue; | |
9 | }); | |
10 | ||
11 | } |
2 | 2 | // See the file 'doc/LICENSE' for the license information |
3 | 3 | function(doc) { |
4 | 4 | if(doc.type == "Vulnerability"){ |
5 | var easeofresolution = "trivial", | |
5 | var easeofresolution = "", | |
6 | 6 | impact = { |
7 | 7 | "accountability": false, |
8 | 8 | "availability": false, |