├── .gitignore ├── README.md ├── install ├── createPostgresDB.py └── testDataPostgres.py ├── lib ├── Config.py ├── Toolkit.py ├── database │ ├── CVESearch.py │ ├── Users.py │ └── VulnManager │ │ ├── Add.py │ │ ├── Calculations.py │ │ ├── Get-Dicts.py │ │ ├── Get-IDs.py │ │ ├── Get-Names.py │ │ ├── Get-Objects.py │ │ ├── Head.py │ │ ├── Tail.py │ │ ├── Update.py │ │ ├── __init-orig__.py │ │ ├── __init__.py │ │ └── merge.py ├── exceptions │ ├── AlreadyExistsException.py │ ├── InvalidSystemGroupInfo.py │ ├── InvalidVariableTypes.py │ ├── TeamRequiredException.py │ ├── UserNotFoundError.py │ └── __init__.py └── objects │ ├── Component.py │ ├── SystemGroup.py │ ├── Ticket.py │ ├── User.py │ └── __init__.py ├── requirements.txt └── sbin ├── static ├── css │ ├── bootstrap.min.css │ └── style.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── img │ └── favicon.ico └── js │ ├── bootstrap.min.js │ ├── custom │ ├── components.js │ ├── scripts.js │ └── status.js │ ├── highcharts │ ├── exporting.js │ ├── highcharts-more.js │ ├── highcharts.js │ ├── mootools-adapter.js │ └── prototype-adapter.js │ ├── html5shiv.js │ └── jquery-1.11.2.min.js ├── templates ├── Error.html ├── Login.html ├── Profile.html ├── Team.html ├── addSystem.html ├── defaultHead.html ├── statistics.html ├── systemTickets.html ├── systems.html ├── teamContent.html ├── test.html ├── ticket.html └── vulnerabilities.html └── webserver.py /.gitignore: -------------------------------------------------------------------------------- 1 | lib/__pycache__/ 2 | lib/database/__pycache__/ 3 | lib/exceptions/__pycache__/ 4 | lib/objects/__pycache__/ 5 | lib/database/VulnManager/__pycache__/ 6 | 7 | lib/database/VulnManager/__init-previous__.py 8 | lib/database/VulnManager/__init-orig__.py 9 | lib/database/VulnManager/__init__.py.bak 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vulnerability-management 2 | This is a prototype for a vulnerability management tool, directly implementing the CVE-Search database for automating the 3 | creation of tickets. 4 | 5 | ##Installation: 6 | ###Requirements 7 | You will need to install postgresql to your repository. It should be in your default repository. On Ubuntu, just type `apt-get install postgresql` in a terminal window.< br /> 8 | You will also need to install `psycopg2` using `sudo pip3 install psycopg2`. A requirements file for `pip3` will soon follow 9 | ###Setting up the database 10 | **Optional modifications:** 11 | If you want, you can change the data at the top of `install/createPostgresDB.py`. Read carefully what you can and cannot edit, and proceed at your own risk. 12 | 13 | **Installation:** 14 | * `cd install` 15 | * `sudo su - postgres` 16 | * `createuser vulnmanager` 17 | * `psql -c "\password vulnmanager"` and set the password to vulnmanager (you can change this later on, or change the passwords in the install script) 18 | * `createdb -O vulnmanager vulnmanager` 19 | * `exit` 20 | * If you run the vulnerability manager on the same server as the postgresql server, change 21 | `local all postgres peer` to `local all postgres md5"` in the `/etc/postgresql//main/pg_hba.conf` file 22 | * Restart the postgres database 23 | * `python3 createPostgresDB.py` 24 | * **Optionally:** `python3 testDataPostgres.py` - add test-data like default users, teams and systems to the database 25 | 26 | 27 | **NOTE** This tool is still in development, and is not yet operational! 28 | **NOTE** Currently, we are only supporting PostgreSQL as a database. If you want support for other database types, we encourage 29 | you to help us build database layers for all sorts of databases. 30 | -------------------------------------------------------------------------------- /install/createPostgresDB.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3.3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Database creation script for PostgreSQL database 5 | # 6 | # Copyright (c) 2015 Pieter-Jan Moreels 7 | # 8 | # Software is free software released under the "Original BSD license" 9 | 10 | # Imports 11 | import os 12 | import sys 13 | _runPath = os.path.dirname(os.path.realpath(__file__)) 14 | sys.path.append(os.path.join(_runPath, "..")) 15 | 16 | import traceback 17 | 18 | 19 | from lib.Config import Configuration as conf 20 | 21 | ############################## 22 | # DEFAULT VALUES & CONSTANTS # 23 | ############################## 24 | # Put all values in lowercase. The program will take care of formatting 25 | # Start closed ticket statusses with "closed-" 26 | # The first one in the list is the default value 27 | # closed-implemented is the option for succesfully implemented patches 28 | def_ticket_statusses=["new", "in progress", "closed-implemented", "closed-irrelevant", "reopened"] 29 | # max_implement_time is in days 30 | def_ticket_priorities=[{"name": "overruled", "max_implement_time": 4, "min_cvss": None}, 31 | {"name": "extreme", "max_implement_time": 7, "min_cvss": 9.0 }, 32 | {"name": "high", "max_implement_time": 14, "min_cvss": 7.0 }, 33 | {"name": "risky", "max_implement_time": 21, "min_cvss": 5.0 }, 34 | {"name": "regular", "max_implement_time": 28, "min_cvss": 2.5 }, 35 | {"name": "low", "max_implement_time": 31, "min_cvss": 0 }] 36 | # Only edit the score, not the name: 37 | def_ticket_urgencies=[{"name": "exploit-kits", "score":20}, 38 | {"name": "exploit-framework", "score":10}, 39 | {"name": "exploit-scripts", "score":5 }, 40 | {"name": "no_known_exploits", "score":1 }] 41 | # Use the access type names from def_access_rights (below) 42 | # Statistics are read only 43 | def_roles=[{"name": "reviewer", "ticket_access": "write-team", "system_group_access": "read-team", "statistics_access": "read-team", "team_access": "read-team", "db_access": False}, 44 | {"name": "teamleader", "ticket_access": "read-team", "system_group_access": "write-team", "statistics_access": "read-team", "team_access": "write-team", "db_access": False}, 45 | {"name": "management", "ticket_access": "none", "system_group_access": "none", "statistics_access": "read-all", "team_access": "none", "db_access": False}, 46 | {"name": "db-engineer", "ticket_access": "write-all", "system_group_access": "write-all", "statistics_access": "read-all", "team_access": "write-all", "db_access": True}] 47 | ############################### 48 | # DO NOT EDIT THESE CONSTANTS # 49 | ############################### 50 | def_ticket_resolutions=["upgrade", "merge", "remove", "patch"] 51 | def_access_rights=["none", "read-team", "write-team", "read-all", "write-all"] 52 | def_teams=[["-", "Placeholder for roles that don't have teams"]] 53 | 54 | def defaultValues(conn): 55 | import lib.database.VulnManager as vmdb 56 | for x in def_ticket_statusses: vmdb.addTicket_Status(x) 57 | for x in def_ticket_resolutions: vmdb.addTicket_Resolution(x) 58 | for x in def_access_rights: vmdb.addAccess_Right(x) 59 | for x in def_ticket_priorities: vmdb.addTicket_Priority(x["name"], x["max_implement_time"], x["min_cvss"]) 60 | for x in def_ticket_urgencies: vmdb.addTicket_Urgency(x["name"], x["score"]) 61 | for x in def_teams: vmdb.addTeam(x[0], x[1]) 62 | for x in def_roles: vmdb.addRole(x["name"], x["ticket_access"], x["system_group_access"], 63 | x["statistics_access"], x["team_access"], x["db_access"]) 64 | 65 | def createTables(conn): 66 | cur = conn.cursor() 67 | # Components 68 | cur.execute("""CREATE TABLE Components( 69 | id serial PRIMARY KEY, 70 | name text NOT NULL UNIQUE);""") 71 | 72 | # System_Group 73 | cur.execute("""CREATE TABLE System_Groups( 74 | id serial PRIMARY KEY, 75 | name text NOT NULL, 76 | team_id int NOT NULL, 77 | info text, 78 | 79 | UNIQUE(name, team_id));""") 80 | 81 | # Tickets 82 | cur.execute("""CREATE TABLE Tickets( 83 | id serial PRIMARY KEY, 84 | history_created_id int UNIQUE, 85 | history_last_id int UNIQUE, 86 | cve text NOT NULL, 87 | system_group_id int NOT NULL, 88 | component_id int NOT NULL, 89 | 90 | UNIQUE(cve, system_group_id, component_id));""") 91 | 92 | 93 | # Ticket_History 94 | cur.execute("""CREATE TABLE Ticket_Histories( 95 | id serial PRIMARY KEY, 96 | ticket_id int NOT NULL, 97 | datetime timestamp NOT NULL, 98 | user_id int, 99 | cvss decimal DEFAULT 0.0, 100 | completion_percentage int DEFAULT 0, 101 | ticket_notes text, 102 | ticket_resolution_id int, 103 | ticket_resolution_fix text, 104 | ticket_status_id int NOT NULL, 105 | ticket_priority_id int, 106 | ticket_urgency_id int);""") 107 | 108 | # Ticket_Statusses 109 | cur.execute("""CREATE TABLE Ticket_Statusses( 110 | id serial PRIMARY KEY, 111 | name text NOT NULL UNIQUE, 112 | description text );""") 113 | 114 | # Ticket_Resolutions 115 | cur.execute("""CREATE TABLE Ticket_Resolutions( 116 | id serial PRIMARY KEY, 117 | name text NOT NULL UNIQUE, 118 | description text );""") 119 | 120 | # Ticket_Priorities 121 | cur.execute("""CREATE TABLE Ticket_Priorities( 122 | id serial PRIMARY KEY, 123 | name text NOT NULL UNIQUE, 124 | max_implement_time int NOT NULL UNIQUE, 125 | min_cvss decimal UNIQUE);""") 126 | 127 | # Ticket_Urgencies 128 | cur.execute("""CREATE TABLE Ticket_Urgencies( 129 | id serial PRIMARY KEY, 130 | name text NOT NULL UNIQUE, 131 | score text NOT NULL );""") 132 | 133 | # Users 134 | cur.execute("""CREATE TABLE Users( 135 | id serial PRIMARY KEY, 136 | user_name text NOT NULL UNIQUE, 137 | first_name text NOT NULL, 138 | last_name text NOT NULL, 139 | password text NOT NULL);""") 140 | 141 | # Team_Types 142 | cur.execute("""CREATE TABLE Roles( 143 | id serial PRIMARY KEY, 144 | name text NOT NULL UNIQUE, 145 | ticket_access_id int NOT NULL, 146 | system_group_access_id int NOT NULL, 147 | statistics_access_id int NOT NULL, 148 | team_access_id int NOT NULL, 149 | db_access boolean NOT NULL);""") 150 | 151 | # Access_Rights 152 | cur.execute("""CREATE TABLE Access_Rights( 153 | id serial PRIMARY KEY, 154 | name text NOT NULL UNIQUE);""") 155 | 156 | # Teams 157 | cur.execute("""CREATE TABLE Teams( 158 | id serial PRIMARY KEY, 159 | name text NOT NULL UNIQUE, 160 | description text NOT NULL);""") 161 | 162 | # Users_in_Teams 163 | cur.execute("""CREATE TABLE Users_in_Team( 164 | user_id int, 165 | team_id int, 166 | role_id int, 167 | 168 | PRIMARY KEY(user_id, team_id, role_id) );""") 169 | 170 | # Components_in_System_Group 171 | cur.execute("""CREATE TABLE Components_in_System_Group( 172 | component_id int, 173 | system_group_id int, 174 | 175 | PRIMARY KEY(component_id, system_group_id) );""") 176 | 177 | conn.commit() 178 | cur.close() 179 | 180 | def addForeignKeys(conn): 181 | cur = conn.cursor() 182 | cur.execute("""ALTER TABLE System_Groups ADD FOREIGN KEY(team_id) REFERENCES Teams(id)""") 183 | 184 | cur.execute("""ALTER TABLE Tickets ADD FOREIGN KEY(component_id) REFERENCES Components(id)""") 185 | cur.execute("""ALTER TABLE Tickets ADD FOREIGN KEY(system_group_id) REFERENCES System_Groups(id)""") 186 | cur.execute("""ALTER TABLE Tickets ADD FOREIGN KEY(history_created_id) REFERENCES Ticket_Histories(id)""") 187 | cur.execute("""ALTER TABLE Tickets ADD FOREIGN KEY(history_last_id) REFERENCES Ticket_Histories(id)""") 188 | 189 | cur.execute("""ALTER TABLE Ticket_Histories ADD FOREIGN KEY(ticket_id) REFERENCES Tickets(id)""") 190 | cur.execute("""ALTER TABLE Ticket_Histories ADD FOREIGN KEY(ticket_status_id) REFERENCES Ticket_Statusses(id)""") 191 | cur.execute("""ALTER TABLE Ticket_Histories ADD FOREIGN KEY(ticket_resolution_id) REFERENCES Ticket_Resolutions(id)""") 192 | cur.execute("""ALTER TABLE Ticket_Histories ADD FOREIGN KEY(user_id) REFERENCES Users(id)""") 193 | cur.execute("""ALTER TABLE Ticket_Histories ADD FOREIGN KEY(ticket_priority_id) REFERENCES Ticket_Priorities(id)""") 194 | cur.execute("""ALTER TABLE Ticket_Histories ADD FOREIGN KEY(ticket_urgency_id) REFERENCES Ticket_Urgencies(id)""") 195 | 196 | cur.execute("""ALTER TABLE Users_in_Team ADD FOREIGN KEY(user_id) REFERENCES Users(id)""") 197 | cur.execute("""ALTER TABLE Users_in_Team ADD FOREIGN KEY(team_id) REFERENCES Teams(id)""") 198 | cur.execute("""ALTER TABLE Users_in_Team ADD FOREIGN KEY(role_id) REFERENCES Roles(id)""") 199 | 200 | cur.execute("""ALTER TABLE Roles ADD FOREIGN KEY(ticket_access_id) REFERENCES Access_Rights(id)""") 201 | cur.execute("""ALTER TABLE Roles ADD FOREIGN KEY(system_group_access_id) REFERENCES Access_Rights(id)""") 202 | cur.execute("""ALTER TABLE Roles ADD FOREIGN KEY(statistics_access_id) REFERENCES Access_Rights(id)""") 203 | cur.execute("""ALTER TABLE Roles ADD FOREIGN KEY(team_access_id) REFERENCES Access_Rights(id)""") 204 | 205 | cur.execute("""ALTER TABLE Components_in_System_Group ADD FOREIGN KEY(system_group_id) REFERENCES System_Groups(id)""") 206 | cur.execute("""ALTER TABLE Components_in_System_Group ADD FOREIGN KEY(component_id) REFERENCES Components(id)""") 207 | 208 | conn.commit() 209 | cur.close() 210 | 211 | try: 212 | conn=conf.getVMpsqlConnection() 213 | createTables(conn) 214 | print("created tables") 215 | addForeignKeys(conn) 216 | print("created foreign keys") 217 | defaultValues(conn) 218 | print("added default values") 219 | print("database creation complete") 220 | 221 | except Exception as e: 222 | print(traceback.format_exc()) 223 | -------------------------------------------------------------------------------- /install/testDataPostgres.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3.3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Database creation script for PostgreSQL database 5 | # 6 | # Copyright (c) 2015 Pieter-Jan Moreels 7 | # 8 | # Software is free software released under the "Original BSD license" 9 | import sys 10 | try: 11 | import os 12 | _runPath = os.path.dirname(os.path.realpath(__file__)) 13 | sys.path.append(os.path.join(_runPath, "..")) 14 | from passlib.hash import pbkdf2_sha256 15 | from lib.Config import Configuration as conf 16 | except Exception as e: 17 | print(e) 18 | sys.exit("Dependencies missing! First run the install script.") 19 | 20 | import traceback 21 | 22 | rounds = conf.getUserEncryptionRounds() 23 | saltLength = conf.getUserSaltLength() 24 | 25 | try: 26 | conn=conf.getVMpsqlConnection() 27 | cur = conn.cursor() 28 | 29 | teams=[ ["Team A", "TestTeam one"], 30 | ["Team B", "TestTeam two"], 31 | ["engineers", "", ] ] 32 | users=[ ["test", "firstname", "lastname", "test", [["Team A", "reviewer" ],["Team B", "reviewer"] ]], 33 | ["teamleader", "John", "Doe", "test", [["Team A", "teamleader" ],["Team A", "reviewer"], ["Team B", "reviewer"]]], 34 | ["engineer", "Foo", "Bar", "test", [["-", "db-engineer"] ]], 35 | ["management", "Foo", "Bar", "test", [["-", "management" ] ]]] 36 | systemgroups=[ ["DNS", "Team A", "Our DNS Servers", ["cpe:2.3:a:libssh:libssh:2.3", 37 | "cpe:2.3:a:openssl:openssl:1.0.1e", 38 | "cpe:2.3:a:haxx:curl:7.31.0" ]], 39 | ["Proxy", "Team A", "", ["cpe:2.3:a:gnu:gnutls:", 40 | "cpe:2.3:a:haxx:curl:7.31.0", 41 | "cpe:2.3:a:openssl:openssl:1.0.1e" ]], 42 | ["Proxy", "Team B", "", ["cpe:2.3:a:gnu:gnutls:", 43 | "cpe:2.3:a:apache:xerces-c%2b%2b", 44 | "cpe:2.3:a:redhat:richfaces", 45 | "cpe:2.3:a:openssl:openssl:1.0.1e" ]], 46 | ["Citrix", "engineers", "Some Servers", ["cpe:/o:microsoft:windows_server_2008:", 47 | "cpe:/a:citrix:netscaler", 48 | "cpe:/a:mcafee:virusscan_enterprise" ]]] 49 | 50 | for x in teams: 51 | cur.execute("""INSERT INTO Teams VALUES(DEFAULT, %s, %s) RETURNING id;""" , (x[0], x[1])) 52 | conn.commit() 53 | for x in users: 54 | pwd=pbkdf2_sha256.encrypt(x[3], rounds=rounds, salt_size=saltLength) 55 | cur.execute("""INSERT INTO Users VALUES(DEFAULT, %s, %s, %s, %s) RETURNING id""", (x[0], x[1], x[2], pwd)) 56 | uid=cur.fetchone()[0] 57 | cur.execute("""SELECT id, name FROM Roles""") 58 | roles={x[1]:x[0] for x in cur.fetchall()} 59 | for y in x[4]: 60 | cur.execute("""INSERT INTO Users_in_Team (user_id, team_id, role_id) 61 | SELECT %s, id, %s 62 | FROM Teams 63 | WHERE name = %s;""" , (uid, roles[y[1]], y[0])) 64 | conn.commit() 65 | for x in systemgroups: 66 | cur.execute("""INSERT INTO System_Groups (name, team_id, info) 67 | SELECT %s, id, %s 68 | FROM Teams 69 | WHERE name = %s 70 | RETURNING id;""" , (x[0], x[2], x[1])) 71 | groupID=cur.fetchall()[0][0] 72 | conn.commit() 73 | for c in x[3]: 74 | cur.execute("""with s as ( 75 | select id, name 76 | from Components 77 | where name = %s 78 | ), i as ( 79 | insert into Components (name) 80 | select %s 81 | where not exists (select 1 from s) 82 | returning id, name 83 | ) 84 | select id, name from i 85 | union all 86 | select id, name from s; """,(c, c)) 87 | componentID=cur.fetchall()[0][0] 88 | try: 89 | cur.execute("""INSERT INTO Components_in_System_Group VALUES(%s, %s);""",(componentID, groupID)) 90 | except: 91 | pass 92 | 93 | 94 | conn.commit() 95 | except Exception as e: 96 | print(traceback.format_exc()) 97 | -------------------------------------------------------------------------------- /lib/Config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3.3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Config reader to read the configuration file 5 | # 6 | # Copyright (c) 2014-2015 Pieter-Jan Moreels 7 | # 8 | # Software is free software released under the "Original BSD license" 9 | 10 | # Imports 11 | import sys 12 | import os 13 | runPath = os.path.dirname(os.path.realpath(__file__)) 14 | try: 15 | import psycopg2 16 | import pymongo 17 | except: 18 | sys.exit("Dependencies missing! First run the install script.") 19 | 20 | import configparser 21 | 22 | class Configuration(): 23 | ConfigParser = configparser.ConfigParser() 24 | ConfigParser.read(os.path.join(runPath, "../etc/configuration.ini")) 25 | default={'csMongoHost': 'localhost', 'csMongoPort': 27017, 'csMongoDB': 'cvedb', 26 | 'vmHost': 'localhost', 'vmPort': 5432, 'VMDB': 'vulnmanager', 27 | 'FlaskHost':'localhost', 'FlaskPort':5001, 'FlaskDebug':True, 28 | 'ssl': False, 'sslCertificate': "./ssl/cve-search.crt", 29 | 'sslKey': "./ssl/cve-search.crt", 30 | 'UserDBRounds':8000 , 'UserDBSaltLength':10} 31 | 32 | @classmethod 33 | def readSetting(cls, section, item, default): 34 | result = default 35 | try: 36 | if type(default) == bool: 37 | result = cls.ConfigParser.getboolean(section, item) 38 | elif type(default) == int: 39 | result = cls.ConfigParser.getint(section, item) 40 | elif type(default) == list: 41 | result = cls.ConfigParser.get(section, item).split(",") 42 | else: 43 | result = cls.ConfigParser.get(section, item) 44 | except: 45 | pass 46 | return result 47 | 48 | # Mongo - CVE-Search 49 | @classmethod 50 | def getCSMongoDB(cls): 51 | return cls.readSetting("CVE-Search", "DB", cls.default['csMongoDB']) 52 | @classmethod 53 | def getCSMongoConnection(cls): 54 | mongoHost = cls.readSetting("CVE-Search", "Host", cls.default['csMongoHost']) 55 | mongoPort = cls.readSetting("CVE-Search", "Port", cls.default['csMongoPort']) 56 | mongoDB = cls.getCSMongoDB() 57 | try: 58 | connect = pymongo.MongoClient(mongoHost, mongoPort) 59 | except: 60 | sys.exit("Unable to connect to Mongo. Is it running on %s:%s?"%(mongoHost,mongoPort)) 61 | return connect[mongoDB] 62 | 63 | # Mongo - Vulnerability Manager 64 | @classmethod 65 | def getVMDB(cls): 66 | return cls.readSetting("Vulnerability-Manager", "DB", cls.default['VMDB']) 67 | @classmethod 68 | def getVMMongoConnection(cls): 69 | mongoHost = cls.readSetting("Vulnerability-Manager", "Host", cls.default['vmHost']) 70 | mongoPort = cls.readSetting("Vulnerability-Manager", "Port", cls.default['vmPort']) 71 | try: 72 | connect = pymongo.MongoClient(mongoHost, mongoPort) 73 | except: 74 | sys.exit("Unable to connect to Mongo. Is it running on %s:%s?"%(mongoHost,mongoPort)) 75 | return connect[cls.getVMDB()] 76 | 77 | # PostgreSQL - Vulnerability Manager 78 | @classmethod 79 | def getVMpsqlConnection(cls): 80 | h = cls.readSetting("Vulnerability-Manager", "Host", cls.default['vmHost']) 81 | p = cls.readSetting("Vulnerability-Manager", "Port", cls.default['vmPort']) 82 | d = cls.getVMDB() 83 | try: 84 | conn = psycopg2.connect("host='%s' port='%s' dbname=%s user=vulnmanager password=vulnmanager"%(h, p, d)) 85 | except Exception as e: 86 | print(e) 87 | sys.exit("Unable to connect to PostgreSQL. Is it running on %s:%s?"%(h,p)) 88 | return conn 89 | 90 | # Encryption for users 91 | @classmethod 92 | def getUserEncryptionRounds(cls): 93 | return cls.readSetting("User-DB", "EncryptionRounds", cls.default['UserDBRounds']) 94 | @classmethod 95 | def getUserSaltLength(cls): 96 | return cls.readSetting("User-DB", "SaltLength", cls.default['UserDBSaltLength']) 97 | 98 | # Flask 99 | @classmethod 100 | def getFlaskHost(cls): 101 | return cls.readSetting("Server", "Host", cls.default['FlaskHost']) 102 | @classmethod 103 | def getFlaskPort(cls): 104 | return cls.readSetting("Server", "Port", cls.default['FlaskPort']) 105 | @classmethod 106 | def getFlaskDebug(cls): 107 | return cls.readSetting("Server", "Debug", cls.default['FlaskDebug']) 108 | 109 | # SSL 110 | @classmethod 111 | def useSSL(cls): 112 | return cls.readSetting("Webserver", "SSL", cls.default['ssl']) 113 | @classmethod 114 | def getSSLCert(cls): 115 | return os.path.join(runPath, "..", cls.readSetting("Webserver", "Certificate", cls.default['sslCertificate'])) 116 | @classmethod 117 | def getSSLKey(cls): 118 | return os.path.join(runPath, "..", cls.readSetting("Webserver", "Key", cls.default['sslKey'])) 119 | -------------------------------------------------------------------------------- /lib/Toolkit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3.3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Toolkit for functions between scripts 5 | # 6 | # Software is free software released under the "Modified BSD license" 7 | # 8 | # Copyright (c) 2014-2015 Pieter-Jan Moreels 9 | 10 | # Note of warning: CPEs like cpe:/o:microsoft:windows_8:-:-:x64 are given to us by Mitre 11 | # x64 will be parsed as Edition in this case, not Architecture 12 | def toStringFormattedCPE(cpe,autofill=False): 13 | cpe=cpe.strip() 14 | if not cpe.startswith('cpe:2.3:'): 15 | if not cpe.startswith('cpe:/'): return False 16 | cpe=cpe.replace('cpe:/','cpe:2.3:') 17 | cpe=cpe.replace('::',':-:') 18 | cpe=cpe.replace('~-','~') 19 | cpe=cpe.replace('~',':-:') 20 | cpe=cpe.replace('::',':') 21 | cpe=cpe.strip(':-') 22 | if autofill: 23 | e=cpe.split(':') 24 | for x in range(0,13-len(e)): 25 | cpe+=':-' 26 | return cpe 27 | 28 | # Function to take either the xth element from list y, or key x from dict y 29 | # Returns None if x or y is none, key is not found, index x out of range or not a valid type 30 | def xFromy(x, y): 31 | try: 32 | return y[x] 33 | except: 34 | return None 35 | 36 | def addToListOfDicts(lst, key, val): 37 | return ([x.update({key: val}) for x in lst],lst)[1] 38 | -------------------------------------------------------------------------------- /lib/database/CVESearch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3.3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Interface for CVE-Search 5 | # > Written for MongoDB 6 | # 7 | # Copyright (c) 2015 Pieter-Jan Moreels 8 | # 9 | # Software is free software released under the "Original BSD license" 10 | 11 | # Imports 12 | import re 13 | 14 | from lib.Config import Configuration as conf 15 | 16 | # Variables 17 | csdb = conf.getCSMongoConnection() 18 | cves = csdb.cves 19 | 20 | # Get 21 | def getCVEInfo(cve): 22 | info=cves.find_one({"id":cve}) 23 | if info: info.pop("_id") 24 | try: 25 | info["cvss"]=float(info["cvss"]) 26 | except: 27 | pass 28 | return info 29 | 30 | def getVulns(cpes, limit): 31 | if not cpes: return [] 32 | regexes = [re.compile(re.escape(x)) for x in cpes] 33 | return list(cves.find({'vulnerable_configuration': {"$in":regexes}}).sort("Modified", -1).limit(limit)) 34 | 35 | def isVulnerable(cpe): 36 | return True if len(list(cves.find({'vulnerable_configuration': cpe}).limit(1)))==1 else False 37 | -------------------------------------------------------------------------------- /lib/database/Users.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3.3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Interface for users in the log-in system 5 | # > Written for MongoDB 6 | # 7 | # Copyright (c) 2015 Pieter-Jan Moreels 8 | # 9 | # Software is free software released under the "Original BSD license" 10 | 11 | # Imports 12 | from passlib.hash import pbkdf2_sha256 13 | 14 | from lib.Config import Configuration as conf 15 | 16 | # Variables 17 | conn = conf.getVMpsqlConnection() 18 | 19 | rounds = conf.getUserEncryptionRounds() 20 | saltLength = conf.getUserSaltLength() 21 | 22 | # Functions 23 | # Decorators 24 | def cursor_wrapped(func): 25 | def func_wrapper(*args, **kwargs): 26 | cur=conn.cursor() 27 | result = func(cur, *args, **kwargs) 28 | cur.close() 29 | return result 30 | return func_wrapper 31 | 32 | @cursor_wrapped 33 | def getUser(cur, username): 34 | cur.execute("""SELECT * FROM Users WHERE user_name=%s;""", (username,)) 35 | c = cur.fetchone() 36 | if not c: return None 37 | teams={} 38 | cur.execute("""SELECT id, name from Teams;""") 39 | t={x[0]: x[1] for x in cur.fetchall()} 40 | cur.execute("""SELECT * from Roles;""") 41 | r={x[0]: [x[1], x[2], x[3], x[4], x[5], x[6]] for x in cur.fetchall()} 42 | cur.execute("""SELECT * from Access_Rights;""") 43 | a={x[0]: x[1] for x in cur.fetchall()} 44 | cur.execute("""SELECT team_id, role_id from Users_in_Team WHERE user_id=%s;""", (c[0],)) 45 | uit=cur.fetchall() 46 | for x in uit: 47 | if x[0] in teams: 48 | teams[x[0]]["roles"].append({"name": r[x[1]][0], 49 | "access":{ "ticket":a[r[x[1]][1]], "system_group": a[r[x[1]][2]], "statistics": a[r[x[1]][3]], "team":a[r[x[1]][4]], "db":r[x[1]][5]}}) 50 | else: 51 | teams[x[0]]={"name":t[x[0]], "roles":[ {"name": r[x[1]][0], 52 | "access":{ "ticket":a[r[x[1]][1]], "system_group": a[r[x[1]][2]], "statistics": a[r[x[1]][3]], "team":a[r[x[1]][4]], "db":r[x[1]][5]}}]} 53 | return {"username": username, "first_name": c[2], "last_name": c[3], "teams": teams.values()} 54 | 55 | @cursor_wrapped 56 | def checkPass(cur, name, pw): 57 | cur.execute("""SELECT * FROM Users WHERE user_name=%s;""", (name,)) 58 | user = cur.fetchone() 59 | return True if user and pbkdf2_sha256.verify(pw, user[4]) else False 60 | 61 | @cursor_wrapped 62 | def addUser(cur, user): 63 | # TODO all user stuff 64 | u=user.getDict(); 65 | pwd=pbkdf2_sha256.encrypt(u["password"], rounds=rounds, salt_size=saltLength) 66 | try: 67 | groupID=cur.execute("""INSERT INTO Users VALUES(DEFAULT, %s, first_name, last_name, password);""" , (u['userID'], u['notes'], j['team'])) 68 | return True 69 | except: 70 | return False 71 | -------------------------------------------------------------------------------- /lib/database/VulnManager/Add.py: -------------------------------------------------------------------------------- 1 | @cursor_wrapped 2 | def addComponent(cur, component): 3 | if type(component) is not str: raise(InvalidVariableTypes) 4 | cur.execute("""WITH s as ( 5 | SELECT id, name 6 | FROM Components 7 | WHERE name = %s 8 | ), i AS ( 9 | INSERT INTO Components (name) 10 | SELECT %s 11 | WHERE NOT EXISTS (SELECT 1 FROM s) 12 | RETURNING id, name 13 | ) SELECT id, name FROM i 14 | UNION ALL 15 | SELECT id, name FROM s; """,(component, component)) 16 | vmdb.commit() 17 | return cur.fetchone()[0] 18 | 19 | @cursor_wrapped 20 | def addSystemGroup(cur, _group): 21 | if type(_group) is not SystemGroup: raise(InvalidVariableTypes) 22 | j=_group.getDict() 23 | try: 24 | groupID=cur.execute("""INSERT INTO System_Groups (name, team_id, info) 25 | SELECT %s, id, %s 26 | FROM Teams 27 | WHERE name = %s 28 | RETURNING id;""" , (j['groupName'], j['notes'], j['team'])) 29 | for c in j["components"]: 30 | componentID=addComponent(c) 31 | try: 32 | cur.execute("""INSERT INTO Components_in_System_Group VALUES(%s, %s);""",(componentID, groupID)) 33 | except: 34 | pass 35 | vmdb.commit() 36 | return groupID 37 | except Exception as e: 38 | print(traceback.format_exc()) 39 | return None 40 | 41 | @cursor_wrapped 42 | def addTicket(cur, _ticket): 43 | if type(_ticket) is not Ticket: raise(InvalidVariableTypes) 44 | t=_ticket.getDict() 45 | try: 46 | cur.execute("""SELECT id FROM System_Groups WHERE name = %s AND team_id IN (SELECT id FROM Teams WHERE name = %s);""",(t["groupName"], t["team"])) 47 | sgid=cur.fetchone()[0] 48 | cur.execute("""SELECT id FROM Components WHERE name = %s;""",(t["vulnComp"]["cpe"],)) 49 | cid=cur.fetchone()[0] 50 | cur.execute("""SELECT id FROM Teams WHERE name = %s;""",(t["team"],)) 51 | tid=cur.fetchone()[0] 52 | priority = calculatePriority(t) 53 | info = cvedb.getCVEInfo(t["cve"]) 54 | cur.execute("""INSERT INTO Tickets VALUES (DEFAULT, %s, %s, %s, %s, %s, %s) RETURNING ID;""",(None, None, t["cve"], sgid, cid, tid)) 55 | ticket_id=cur.fetchone()[0] 56 | cur.execute("""INSERT INTO Ticket_Histories(ticket_id, datetime, cvss, ticket_status_id, ticket_priority_id) VALUES(%s, %s, %s, %s, %s) RETURNING ID;""", 57 | (ticket_id, datetime.fromtimestamp(time.time()), info["cvss"], _DEFAULT_STATUS, priority)) 58 | history=cur.fetchone()[0] 59 | cur.execute("""UPDATE Tickets SET history_created_id=%s, history_last_id=%s WHERE id=%s"""%(ticket_id, ticket_id, history)) 60 | vmdb.commit() 61 | return ticket_id 62 | except Exception as e: 63 | print(traceback.format_exc()) 64 | return None 65 | 66 | @cursor_wrapped 67 | def addUser(cur, _user): 68 | # TODO all user stuff 69 | u=_user.getDict(); 70 | pwd=pbkdf2_sha256.encrypt(u["password"], rounds=rounds, salt_size=saltLength) 71 | try: 72 | groupID=cur.execute("""INSERT INTO Users VALUES(DEFAULT, %s, first_name, last_name, password);""" , (u['userID'], u['notes'], j['team'])) 73 | return groupID 74 | except: 75 | return None 76 | 77 | @cursor_wrapped 78 | def addTicket_Status(cur, status): 79 | if type(status) is not str: raise(InvalidVariableTypes) 80 | cur.execute("INSERT INTO Ticket_Statusses VALUES(DEFAULT, %s) RETURNING id",(status,)) 81 | vmdb.commit() 82 | return cur.fetchone()[0] 83 | 84 | @cursor_wrapped 85 | def addTicket_Resolution(cur, resolution): 86 | if type(resolution) is not str: raise(InvalidVariableTypes) 87 | cur.execute("INSERT INTO Ticket_Resolutions VALUES(DEFAULT, %s) RETURNING id",(resolution,)) 88 | vmdb.commit() 89 | return cur.fetchone()[0] 90 | 91 | @cursor_wrapped 92 | def addAccess_Right(cur, access_right): 93 | if type(access_right) is not str: raise(InvalidVariableTypes) 94 | cur.execute("INSERT INTO Access_Rights VALUES(DEFAULT, %s) RETURNING id",(access_right,)) 95 | vmdb.commit() 96 | return cur.fetchone()[0] 97 | 98 | @cursor_wrapped 99 | def addTicket_Priority(cur, name, max_implement_time, min_cvss): 100 | if type(min_cvss) is int: min_cvss = float(min_cvss) 101 | if ((type(name) is not str) or (type(max_implement_time) is not int) or 102 | (type(min_cvss) is not float and type(min_cvss) is not decimal.Decimal and 103 | min_cvss is not None)): 104 | raise(InvalidVariableTypes) 105 | cur.execute("INSERT INTO Ticket_Priorities VALUES(DEFAULT, %s, %s, %s) RETURNING id", 106 | (name, max_implement_time, min_cvss)) 107 | vmdb.commit() 108 | return cur.fetchone()[0] 109 | 110 | @cursor_wrapped 111 | def addTicket_Urgency(cur, name, score): 112 | if ((type(name) is not str) or (type(score) is not int)): raise(InvalidVariableTypes) 113 | cur.execute("INSERT INTO Ticket_Urgencies VALUES(DEFAULT, %s, %s) RETURNING id",(name, score)) 114 | vmdb.commit() 115 | return cur.fetchone()[0] 116 | 117 | @cursor_wrapped 118 | def addTeam(cur, name, description): 119 | if ((type(name) is not str) or (type(description) is not str)): raise(InvalidVariableTypes) 120 | cur.execute("INSERT INTO Teams VALUES(DEFAULT, %s, %s) RETURNING id",(name, description)) 121 | vmdb.commit() 122 | return cur.fetchone()[0] 123 | 124 | @cursor_wrapped 125 | def addRole(cur, name, ticket_access, system_group_access, statistics_access, team_access, db_access): 126 | a = getAccessRights() 127 | if (not all(isinstance(x,str) for x in [name, ticket_access, system_group_access, statistics_access, team_access]) or 128 | (type(db_access) is not bool)): raise(InvalidVariableTypes) 129 | try: 130 | cur.execute("INSERT INTO Roles VALUES(DEFAULT, %s, %s, %s, %s, %s, %s);", 131 | (name, a[ticket_access], a[system_group_access], a[statistics_access], a[team_access], db_access)) 132 | vmdb.commit() 133 | return cur.fetchone()[0] 134 | except: 135 | return None 136 | -------------------------------------------------------------------------------- /lib/database/VulnManager/Calculations.py: -------------------------------------------------------------------------------- 1 | def calculatePriority(_ticket): 2 | priority=None 3 | try: 4 | cvss=float(_ticket.cveInfo["cvss"]) 5 | except: 6 | return None 7 | # TODO might to reverse loop to fix None problem 8 | for x in getPriorityLevels(): 9 | if x[1]= start and aDate < end: 38 | if action["status"] == _DEFAULT_STATUS_TEXT: 39 | new+=1 40 | opened=True 41 | elif action["status"].lower().startswith("closed"): 42 | if opened: 43 | oac+=1 44 | new-=1 45 | else: 46 | closed+=1 47 | systems.append({"systems":x["groupName"], "new": new, "closed": closed, "openedAndClosed":oac}) 48 | return systems 49 | 50 | @cursor_wrapped 51 | def getTeamDicts(cur): 52 | cur.execute("SELECT id, name FROM Teams;") 53 | return {x[0]: x[1] for x in cur.fetchall()} 54 | 55 | @cursor_wrapped 56 | def getAccessRights(cur): 57 | cur.execute("SELECT * FROM Access_Rights;") 58 | return {v: k for k, v in dict(cur.fetchall()).items()} 59 | -------------------------------------------------------------------------------- /lib/database/VulnManager/Get-IDs.py: -------------------------------------------------------------------------------- 1 | # Object ID's 2 | @cursor_wrapped 3 | def getSystemGroupID(cur, name, team): 4 | cur.execute("""SELECT System_Groups.id 5 | FROM System_Groups, Teams 6 | WHERE System_Groups.name = %s 7 | and Teams.name = %s 8 | and System_Groups.team_id = Teams.id;""", (name, team)) 9 | sid=cur.fetchone() 10 | return sid[0] if sid else None 11 | 12 | 13 | @cursor_wrapped 14 | def getComponentID(cur, name): 15 | cur.execute("""SELECT id FROM Components WHERE name = %s;""", (name, )) 16 | cid=cur.fetchone() 17 | return cid[0] if cid else None 18 | 19 | 20 | # List of ID's 21 | @cursor_wrapped 22 | def getPriorityLevels(cur): 23 | cur.execute("""SELECT id, min_cvss FROM Ticket_Priorities WHERE min_cvss IS NOT NULL;""") 24 | # Sort from highest CVSS to lowest 25 | priorities=sorted(cur.fetchall(),key=lambda x: x[1]) 26 | return priorities 27 | -------------------------------------------------------------------------------- /lib/database/VulnManager/Get-Names.py: -------------------------------------------------------------------------------- 1 | @cursor_wrapped 2 | def getComponentNames(cur, team=None, system=None): 3 | if team and system: 4 | cur.execute("""SELECT name FROM Components WHERE id IN ( 5 | SELECT component_ID FROM Components_in_System_Group WHERE system_group_id IN ( 6 | SELECT id FROM System_Group WHERE team_id IN ( 7 | SELECT id FROM Teams WHERE name = %s ) AND name = %s;));""", (team,system)) 8 | else: 9 | cur.execute("""SELECT name FROM Components;""") 10 | c=[x[0] for x in cur.fetchall()] 11 | return sorted(list(set(c)), key=lambda s: s.lower()) 12 | 13 | 14 | @cursor_wrapped 15 | def getCPENames(cur, team=None): 16 | query="SELECT name FROM Components " 17 | if team: 18 | query+="""WHERE id IN( 19 | SELECT component_id FROM Components_in_System_Group WHERE system_group_id IN( 20 | SELECT id from System_Groups WHERE team_id IN( 21 | SELECT id from Teams where name = %s)))""" 22 | cur.execute(query, list(filter(None, [team]))) 23 | cpes=[x[0] for x in cur.fetchall()] 24 | return list(set(itertools.chain(*cpes))) 25 | 26 | 27 | @cursor_wrapped 28 | def getStatusses(cur): 29 | cur.execute("""SELECT name FROM Ticket_Statusses;""") 30 | return [x[0] for x in cur.fetchall()] 31 | 32 | 33 | @cursor_wrapped 34 | def getTeamNames(cur): 35 | cur.execute("SELECT name FROM Teams;") 36 | return [x[0] for x in cur.fetchall()] 37 | -------------------------------------------------------------------------------- /lib/database/VulnManager/Get-Objects.py: -------------------------------------------------------------------------------- 1 | @cursor_wrapped 2 | def getTicket(cur, cve, cpe, system, team): 3 | cur.execute("""SELECT Tickets.id, Teams.name, System_Groups.name, Tickets.cve, Components.name, 4 | Ticket_Statusses.name, Ticket_Histories.ticket_notes, Ticket_Histories.ticket_resolution_id, 5 | Ticket_Histories.ticket_resolution_fix, Ticket_Histories.cvss, Ticket_Histories.ticket_priority_id 6 | FROM Tickets, System_Groups, Components, Teams, Ticket_Histories, Ticket_Resolutions, Ticket_Statusses 7 | WHERE cve=%s 8 | AND component_id IN ( SELECT id FROM Components WHERE name = %s ) 9 | AND system_group_id IN ( SELECT id FROM System_Groups WHERE name = %s 10 | AND team_id IN ( SELECT id FROM Teams WHERE name = %s )) 11 | AND Teams.id = System_Groups.team_id 12 | AND System_Groups.id = Tickets.system_group_id 13 | AND Components.id = Tickets.component_id 14 | AND Ticket_Histories.id = Tickets.history_last_id 15 | AND Ticket_Statusses.id = Ticket_Histories.ticket_status_id;""", (cve, cpe, system, team)) 16 | data=cur.fetchone() 17 | c=list(data) if data else data 18 | 19 | if c: 20 | if c[10]: 21 | cur.execute("SELECT name FROM Ticket_Priorities where id=%s", (c[10],)) 22 | priority=(cur.fetchone())[0] 23 | else: 24 | priority=None 25 | cur.execute("SELECT name FROM Ticket_Resolutions") 26 | t_res= [x[0] for x in cur.fetchall()] 27 | cur.execute("SELECT name FROM Ticket_Priorities") 28 | t_prior= [x[0] for x in cur.fetchall()] 29 | cur.execute("SELECT name FROM Ticket_Urgencies") 30 | t_urgent= [x[0] for x in cur.fetchall()] 31 | cur.execute("""SELECT datetime, Users.user_name, cvss, ticket_notes, Ticket_Statusses.name, ticket_resolution_id, ticket_resolution_fix, 32 | ticket_priority_id, ticket_urgency_id 33 | FROM Ticket_Histories, Ticket_Statusses, Users 34 | WHERE ticket_id=%s 35 | AND Ticket_Statusses.id = Ticket_Histories.ticket_status_id 36 | AND Users.id = Ticket_Histories.user_id;""", (c[0],)) 37 | histories=cur.fetchall() 38 | h=[] 39 | for x in histories: 40 | h.append({"time": x[0], "user": x[1], "cvss": x[2], "notes": x[3], "status": x[4], "update": {"type": xFromy(x[5], t_res), "cpe": x[6]}, 41 | "priority": xFromy(x[7], t_prior), "urgency": xFromy(x[8], t_urgent)}) 42 | if c[7]: 43 | cur.execute("""SELECT name FROM Ticket_Resolutions WHERE id = %s""", (c[7],)) 44 | c[7]=cur.fetchone()[0] 45 | c=Ticket(c[1], c[2], c[3], c[4], status=c[5], notes=c[6], history=h, resolution=c[7], resolution_fix=c[8], cvss=c[9], priority=priority, objID=c[0]) 46 | else: 47 | tick=Ticket(team, system, cve, cpe) 48 | addTicket(tick) 49 | c=getTicket(tick.cve, tick.vulnComp["cpe"], tick.groupName, tick.team) 50 | c.addInfo(cvedb.getCVEInfo(cve)) 51 | updatePriority(c) 52 | return c 53 | 54 | 55 | @cursor_wrapped 56 | def getTickets(cur, team, system=None, limit=None): 57 | # Build up statement 58 | command = """SELECT Tickets.cve, Components.name, System_Groups.name, Teams.name 59 | FROM Tickets, System_Groups, Components, Teams, Ticket_histories 60 | WHERE System_Groups.id = Tickets.system_group_id 61 | AND Components.id = Tickets.component_id 62 | AND Teams.id = System_Groups.team_id 63 | AND Ticket_Histories.id = Tickets.history_last_id """ 64 | if limit and limit.lower()=="open": limit = """AND Ticket_Histories.ticket_status_id IN 65 | ( SELECT id FROM Ticket_Statusses WHERE name NOT LIKE 'closed-%%' ) """ 66 | elif limit and limit.lower()=="closed": limit = """AND Ticket_Histories.ticket_status_id IN 67 | ( SELECT id FROM Ticket_Statusses WHERE name LIKE 'closed-%%' ) """ 68 | else: limit = "" 69 | # Execute statement based on system or all systems 70 | if system: 71 | cur.execute(command + limit + """AND system_group_id IN ( SELECT id FROM System_Groups WHERE name = %s 72 | AND team_id IN ( SELECT id FROM Teams WHERE name = %s ))""", (system, team)) 73 | else: 74 | cur.execute(command + limit + """AND system_group_id IN ( SELECT id FROM System_Groups WHERE team_id 75 | IN ( SELECT id FROM Teams WHERE name = %s ))""", (team, )) 76 | return [getTicket(x[0],x[1],x[2],x[3]) for x in cur.fetchall()] 77 | 78 | 79 | @cursor_wrapped 80 | def getVulnsForSystem(cur, groupName, systemTeam, cpes=None): 81 | if type(cpes) is not list and cpes:cpes=[cpes] 82 | if not cpes: 83 | cur.execute("""SELECT name FROM Components WHERE id IN ( 84 | SELECT component_id FROM Components_in_System_Group WHERE system_group_id IN ( 85 | SELECT id FROM System_Groups WHERE name = %s AND team_id IN ( 86 | SELECT id FROM Teams WHERE name = %s )));""",(groupName, systemTeam)) 87 | cpes=[x[0] for x in cur.fetchall()] 88 | 89 | # Get tickets for every cve for the vulnerable systems 90 | tickets=[] 91 | for cpe in cpes: 92 | tickets.extend([getTicket(cve, cpe, groupName, systemTeam) for cve in [x["id"] for x in cvedb.getVulns([cpe], 0)]]) 93 | tickets=[x for x in tickets if not x.status.lower().startswith("closed")] 94 | return tickets 95 | 96 | 97 | @cursor_wrapped 98 | def getGroups(cur, team, group=None): 99 | teams=getTeamDicts() 100 | groups=[] 101 | query = """SELECT * FROM System_Groups WHERE team_id IN (SELECT id FROM Teams WHERE name=%s)""" 102 | if group: query+= "AND name=%s;" 103 | cur.execute(query, list(filter(None, [team, group]))) 104 | for g in cur.fetchall(): 105 | clist=[] 106 | cur.execute("""SELECT * FROM Components WHERE id IN ( 107 | SELECT component_ID FROM Components_in_System_Group WHERE system_group_id = %s );""", (g[0],)) 108 | for comp in cur.fetchall(): 109 | clist.append(Component(comp[1])) 110 | groups.append(SystemGroup(None, g[1], teams[g[2]], None, [], clist)) 111 | return groups 112 | 113 | 114 | @cursor_wrapped 115 | def getGroupDetails(cur, group): 116 | cur.execute("""SELECT name FROM Teams WHERE id=%s;""", (group[2],)) 117 | tid=cur.fetchone()[0] 118 | clist=[] 119 | cur.execute("""SELECT * FROM Components WHERE id IN ( SELECT component_ID FROM Components_in_System_Group WHERE system_group_id = %s );""", (group[0],)) 120 | for comp in cur.fetchall(): 121 | clist.append(Component(comp[1])) 122 | return SystemGroup(None, group[1], tid, None, [], clist) 123 | 124 | 125 | @cursor_wrapped 126 | def getTicketsForSystemComponent(cur, component, _system): 127 | tickets=getTickets(team, _system.groupName) 128 | #for x in tickets: 129 | # print(x) 130 | #tickets.append(getTicket(x[0], x[1], x[2], x[3])) 131 | return tickets 132 | 133 | -------------------------------------------------------------------------------- /lib/database/VulnManager/Head.py: -------------------------------------------------------------------------------- 1 | # Imports 2 | import copy 3 | import decimal 4 | import itertools 5 | import re 6 | import time 7 | import traceback 8 | import sys 9 | from datetime import date 10 | from datetime import datetime 11 | from passlib.hash import pbkdf2_sha256 12 | 13 | from lib.objects import SystemGroup, Component, Ticket 14 | from lib.Config import Configuration as conf 15 | from lib.exceptions import InvalidVariableTypes 16 | from lib.Toolkit import xFromy 17 | 18 | import lib.database.CVESearch as cvedb 19 | 20 | # Variables 21 | vmdb = conf.getVMpsqlConnection() 22 | 23 | # Decorators 24 | def cursor_wrapped(func): 25 | def func_wrapper(*args, **kwargs): 26 | cur=vmdb.cursor() 27 | result = func(cur, *args, **kwargs) 28 | cur.close() 29 | return result 30 | return func_wrapper 31 | 32 | -------------------------------------------------------------------------------- /lib/database/VulnManager/Tail.py: -------------------------------------------------------------------------------- 1 | # Constants 2 | statusses = getStatusses() 3 | _DEFAULT_STATUS = 1 4 | _DEFAULT_STATUS_TEXT=statusses[0] if statusses else None # None when creating the DB 5 | del statusses 6 | -------------------------------------------------------------------------------- /lib/database/VulnManager/Update.py: -------------------------------------------------------------------------------- 1 | @cursor_wrapped 2 | def setDBTicketStatus(cur, ticket, user, status=None, notes=None, update=None, updateType=None, ): 3 | #Allows us to work both with dict and Ticket types 4 | t=ticket.getDict() if type(ticket) == Ticket else ticket 5 | if type(ticket) == Ticket: t=getTicket(t["cve"], t["vulnComp"]["cpe"], t["groupName"], t["team"]).getDict() 6 | 7 | # Prevent unnecesarry database operations 8 | if all(None == i for i in [status, notes, update, updateType]): return False 9 | if status and t["status"] == status.strip(): return False 10 | if notes and t["notes"] == notes.strip(): return False 11 | if update and t["vulnComp"]["update"] == update.strip(): return False 12 | if updateType and t["vulnComp"]["type"] == updateType.strip(): return False 13 | # Fetch the current ticket ID and history 14 | cur.execute("""SELECT id, history_last_id FROM Tickets WHERE cve=%s 15 | AND component_id IN ( SELECT id FROM Components WHERE name = %s ) 16 | AND system_group_id IN ( SELECT id FROM System_Groups WHERE name = %s 17 | AND team_id IN ( SELECT id FROM Teams WHERE name = %s ));""", (t["cve"], t["vulnComp"]["cpe"], t["groupName"], t["team"])) 18 | x=cur.fetchone() 19 | if not x: return False 20 | t_id, h_id = x 21 | # Fetch all the ticket history information 22 | cur.execute("""SELECT * FROM Ticket_Histories WHERE id = %s;""", (h_id,)) 23 | h=cur.fetchone() 24 | # Get ID's of settings, or get the old id's 25 | if status == None: 26 | status=t["status"] 27 | cur.execute("""SELECT id FROM Ticket_Statusses WHERE name = %s;""", (status,)) 28 | status=cur.fetchone()[0] 29 | notes = t["notes"] if notes == None else notes 30 | update = t["vulnComp"]["update"] if update == None else update 31 | if updateType == None: updateType=t["vulnComp"]["type"] 32 | cur.execute("""SELECT id FROM Ticket_Resolutions WHERE name = %s;""", (updateType,)) 33 | updateType=cur.fetchone() 34 | updateType=updateType[0] if updateType else None 35 | # Get user ID 36 | cur.execute("""SELECT id FROM Users WHERE user_name = %s""", (user,)) 37 | uid=cur.fetchone()[0] 38 | # Now make a new entry using above data 39 | now = datetime.fromtimestamp(time.time()) 40 | cur.execute("""INSERT INTO Ticket_Histories VALUES(DEFAULT, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING id;""", 41 | (h[1], now, uid, h[4], h[5], notes, updateType, update, status, h[10], h[11] )) 42 | last_id=cur.fetchone()[0] 43 | cur.execute("""UPDATE Tickets SET history_last_id = %s WHERE id = %s;""", (last_id, t_id)) 44 | vmdb.commit() 45 | return True 46 | 47 | 48 | @cursor_wrapped 49 | def updateCPE(cur, _ticket): 50 | # temporary disable this function 51 | return 52 | # TODO: Verify if this structure won't cause Race conditions 53 | t=_ticket.getDict() 54 | update=t['vulnComp'] 55 | cid=addComponent(update['update']) 56 | sid=getSystemGroupID(t['groupName'], t['team']) 57 | ocid=getComponentID(update['cpe']) 58 | if update['type'] in ["upgrade", "merge"]: 59 | cur.execute("""UPDATE Components_in_System_Group 60 | SET component_id = %s 61 | WHERE component_id = %s 62 | and system_group_id = %s;""", (cid, ocid, sid)) 63 | vmdb.commit() 64 | elif update["type"] == "remove": 65 | cur.execute("""DELETE FROM Components_in_System_Group 66 | WHERE component_id = %s 67 | and system_group_id = %s;""", (ocid, sid)) 68 | vmdb.commit() 69 | # Else we assume patch, in which case nothing needs to happen, only return cpes. 70 | 71 | # Find list of related tickets 72 | return getTicketsForSystemComponent(update['cpe'], t['groupName'], t['team']) 73 | 74 | 75 | @cursor_wrapped 76 | def updatePriority(cur, _ticket): 77 | cvss=_ticket.cveInfo["cvss"] 78 | if _ticket.cvss == cvss: 79 | return 80 | priority=calculatePriority(ticket) 81 | #if priority 82 | -------------------------------------------------------------------------------- /lib/database/VulnManager/__init-orig__.py: -------------------------------------------------------------------------------- 1 | # -- Head.py -- 2 | # Imports 3 | import copy 4 | import decimal 5 | import itertools 6 | import re 7 | import time 8 | import traceback 9 | import sys 10 | from datetime import date 11 | from datetime import datetime 12 | from passlib.hash import pbkdf2_sha256 13 | 14 | from lib.objects import SystemGroup, Component, Ticket 15 | from lib.Config import Configuration as conf 16 | from lib.exceptions import InvalidVariableTypes 17 | from lib.Toolkit import xFromy 18 | 19 | import lib.database.CVESearch as cvedb 20 | 21 | # Variables 22 | vmdb = conf.getVMpsqlConnection() 23 | 24 | # Decorators 25 | def cursor_wrapped(func): 26 | def func_wrapper(*args, **kwargs): 27 | cur=vmdb.cursor() 28 | result = func(cur, *args, **kwargs) 29 | cur.close() 30 | return result 31 | return func_wrapper 32 | 33 | # -- Add.py -- 34 | @cursor_wrapped 35 | def addComponent(cur, component): 36 | if type(component) is not str: raise(InvalidVariableTypes) 37 | cur.execute("""WITH s as ( 38 | SELECT id, name 39 | FROM Components 40 | WHERE name = %s 41 | ), i AS ( 42 | INSERT INTO Components (name) 43 | SELECT %s 44 | WHERE NOT EXISTS (SELECT 1 FROM s) 45 | RETURNING id, name 46 | ) SELECT id, name FROM i 47 | UNION ALL 48 | SELECT id, name FROM s; """,(component, component)) 49 | vmdb.commit() 50 | return cur.fetchone()[0] 51 | 52 | @cursor_wrapped 53 | def addSystemGroup(cur, _group): 54 | if type(_group) is not SystemGroup: raise(InvalidVariableTypes) 55 | j=_group.getDict() 56 | try: 57 | groupID=cur.execute("""INSERT INTO System_Groups (name, team_id, info) 58 | SELECT %s, id, %s 59 | FROM Teams 60 | WHERE name = %s 61 | RETURNING id;""" , (j['groupName'], j['notes'], j['team'])) 62 | for c in j["components"]: 63 | componentID=addComponent(c) 64 | try: 65 | cur.execute("""INSERT INTO Components_in_System_Group VALUES(%s, %s);""",(componentID, groupID)) 66 | except: 67 | pass 68 | vmdb.commit() 69 | return groupID 70 | except Exception as e: 71 | print(traceback.format_exc()) 72 | return None 73 | 74 | @cursor_wrapped 75 | def addTicket(cur, _ticket): 76 | if type(_ticket) is not Ticket: raise(InvalidVariableTypes) 77 | t=_ticket.getDict() 78 | try: 79 | cur.execute("""SELECT id FROM System_Groups WHERE name = %s AND team_id IN (SELECT id FROM Teams WHERE name = %s);""",(t["groupName"], t["team"])) 80 | sgid=cur.fetchone()[0] 81 | cur.execute("""SELECT id FROM Components WHERE name = %s;""",(t["vulnComp"]["cpe"],)) 82 | cid=cur.fetchone()[0] 83 | cur.execute("""SELECT id FROM Teams WHERE name = %s;""",(t["team"],)) 84 | tid=cur.fetchone()[0] 85 | priority = calculatePriority(t) 86 | info = cvedb.getCVEInfo(t["cve"]) 87 | cur.execute("""INSERT INTO Tickets VALUES (DEFAULT, %s, %s, %s, %s, %s, %s) RETURNING ID;""",(None, None, t["cve"], sgid, cid, tid)) 88 | ticket_id=cur.fetchone()[0] 89 | cur.execute("""INSERT INTO Ticket_Histories(ticket_id, datetime, cvss, ticket_status_id, ticket_priority_id) VALUES(%s, %s, %s, %s, %s) RETURNING ID;""", 90 | (ticket_id, datetime.fromtimestamp(time.time()), info["cvss"], _DEFAULT_STATUS, priority)) 91 | history=cur.fetchone()[0] 92 | cur.execute("""UPDATE Tickets SET history_created_id=%s, history_last_id=%s WHERE id=%s"""%(ticket_id, ticket_id, history)) 93 | vmdb.commit() 94 | return ticket_id 95 | except Exception as e: 96 | print(traceback.format_exc()) 97 | return None 98 | 99 | @cursor_wrapped 100 | def addUser(cur, _user): 101 | # TODO all user stuff 102 | u=_user.getDict(); 103 | pwd=pbkdf2_sha256.encrypt(u["password"], rounds=rounds, salt_size=saltLength) 104 | try: 105 | groupID=cur.execute("""INSERT INTO Users VALUES(DEFAULT, %s, first_name, last_name, password);""" , (u['userID'], u['notes'], j['team'])) 106 | return groupID 107 | except: 108 | return None 109 | 110 | @cursor_wrapped 111 | def addTicket_Status(cur, status): 112 | if type(status) is not str: raise(InvalidVariableTypes) 113 | cur.execute("INSERT INTO Ticket_Statusses VALUES(DEFAULT, %s) RETURNING id",(status,)) 114 | vmdb.commit() 115 | return cur.fetchone()[0] 116 | 117 | @cursor_wrapped 118 | def add_ticket_Resolutions(cur, resolution): 119 | if type(resolution) is not str: raise(InvalidVariableTypes) 120 | cur.execute("INSERT INTO Ticket_Resolutions VALUES(DEFAULT, %s) RETURNING id",(resolution,)) 121 | vmdb.commit() 122 | return cur.fetchone()[0] 123 | 124 | @cursor_wrapped 125 | def addAccess_Right(cur, access_right): 126 | if type(access_right) is not str: raise(InvalidVariableTypes) 127 | cur.execute("INSERT INTO Access_Rights VALUES(DEFAULT, %s) RETURNING id",(access_right,)) 128 | vmdb.commit() 129 | return cur.fetchone()[0] 130 | 131 | def addTicket_Priority(cur, name, max_implement_time, min_cvss): 132 | if ((type(name) is not str) or (type(max_implement_time) is not int) or 133 | (type(min_cvss) is not float and type(min_cvss) is not decimal.Decimal)): 134 | raise(InvalidVariableTypes) 135 | cur.execute("INSERT INTO Ticket_Priorities VALUES(DEFAULT, %s, %s, %s) RETURNING id", 136 | (name, max_implement_time, min_cvss)) 137 | vmdb.commit() 138 | return cur.fetchone()[0] 139 | 140 | def addTicket_Urgency(cur, name, score): 141 | if ((type(name) is not str) or (type(score) is not int)): raise(InvalidVariableTypes) 142 | cur.execute("INSERT INTO Ticket_Urgencies VALUES(DEFAULT, %s, %s) RETURNING id",(name, score)) 143 | vmdb.commit() 144 | return cur.fetchone()[0] 145 | 146 | def addTeam(cur, name, description): 147 | if ((type(name) is not str) or (type(description) is not int)): raise(InvalidVariableTypes) 148 | cur.execute("INSERT INTO Teams VALUES(DEFAULT, %s, %s) RETURNING id",(name, description)) 149 | vmdb.commit() 150 | return cur.fetchone()[0] 151 | 152 | def addRole(cur, name, ticket_access, system_group_access, statistics_access, team_access, db_access): 153 | a = getAccessRights() 154 | if (not all(isinstance(x,str) for x in [name, ticket_access, system_group_access, statistics_access, team_access]) or 155 | (type(db_access) is not bool)): raise(InvalidVariableTypes) 156 | try: 157 | cur.execute("INSERT INTO Roles VALUES(DEFAULT, %s, %s, %s, %s, %s, %s);", 158 | (name, a[ticket_access], a[system_group_access], a[statistics_access], a[team_access], db_access)) 159 | vmdb.commit() 160 | return cur.fetchone()[0] 161 | except: 162 | return None 163 | # -- Get-IDs.py -- 164 | # Object ID's 165 | @cursor_wrapped 166 | def getSystemGroupID(cur, name, team): 167 | cur.execute("""SELECT System_Groups.id 168 | FROM System_Groups, Teams 169 | WHERE System_Groups.name = %s 170 | and Teams.name = %s 171 | and System_Groups.team_id = Teams.id;""", (name, team)) 172 | sid=cur.fetchone() 173 | return sid[0] if sid else None 174 | 175 | 176 | @cursor_wrapped 177 | def getComponentID(cur, name): 178 | cur.execute("""SELECT id FROM Components WHERE name = %s;""", (name, )) 179 | cid=cur.fetchone() 180 | return cid[0] if cid else None 181 | 182 | 183 | # List of ID's 184 | @cursor_wrapped 185 | def getPriorityLevels(cur): 186 | cur.execute("""SELECT id, min_cvss FROM Ticket_Priorities WHERE min_cvss IS NOT NULL;""") 187 | # Sort from highest CVSS to lowest 188 | priorities=sorted(cur.fetchall(),key=lambda x: x[1]) 189 | return priorities 190 | # -- Get-Names.py -- 191 | @cursor_wrapped 192 | def getComponentNames(cur, team=None, system=None): 193 | if team and system: 194 | cur.execute("""SELECT name FROM Components WHERE id IN ( 195 | SELECT component_ID FROM Components_in_System_Group WHERE system_group_id IN ( 196 | SELECT id FROM System_Group WHERE team_id IN ( 197 | SELECT id FROM Teams WHERE name = %s ) AND name = %s;));""", (team,system)) 198 | else: 199 | cur.execute("""SELECT name FROM Components;""") 200 | c=[x[0] for x in cur.fetchall()] 201 | return sorted(list(set(c)), key=lambda s: s.lower()) 202 | 203 | 204 | @cursor_wrapped 205 | def getCPENames(cur, team=None): 206 | query="SELECT name FROM Components " 207 | if team: 208 | query+="""WHERE id IN( 209 | SELECT component_id FROM Components_in_System_Group WHERE system_group_id IN( 210 | SELECT id from System_Groups WHERE team_id IN( 211 | SELECT id from Teams where name = %s)))""" 212 | cur.execute(query, list(filter(None, [team]))) 213 | cpes=[x[0] for x in cur.fetchall()] 214 | return list(set(itertools.chain(*cpes))) 215 | 216 | 217 | @cursor_wrapped 218 | def getStatusses(cur): 219 | cur.execute("""SELECT name FROM Ticket_Statusses;""") 220 | return [x[0] for x in cur.fetchall()] 221 | 222 | 223 | @cursor_wrapped 224 | def getTeamNames(cur): 225 | cur.execute("SELECT name FROM Teams;") 226 | return [x[0] for x in cur.fetchall()] 227 | # -- Get-Objects.py -- 228 | @cursor_wrapped 229 | def getTicket(cur, cve, cpe, system, team): 230 | cur.execute("""SELECT Tickets.id, Teams.name, System_Groups.name, Tickets.cve, Components.name, 231 | Ticket_Statusses.name, Ticket_Histories.ticket_notes, Ticket_Histories.ticket_resolution_id, 232 | Ticket_Histories.ticket_resolution_fix, Ticket_Histories.cvss, Ticket_Histories.ticket_priority_id 233 | FROM Tickets, System_Groups, Components, Teams, Ticket_Histories, Ticket_Resolutions, Ticket_Statusses 234 | WHERE cve=%s 235 | AND component_id IN ( SELECT id FROM Components WHERE name = %s ) 236 | AND system_group_id IN ( SELECT id FROM System_Groups WHERE name = %s 237 | AND team_id IN ( SELECT id FROM Teams WHERE name = %s )) 238 | AND Teams.id = System_Groups.team_id 239 | AND System_Groups.id = Tickets.system_group_id 240 | AND Components.id = Tickets.component_id 241 | AND Ticket_Histories.id = Tickets.history_last_id 242 | AND Ticket_Statusses.id = Ticket_Histories.ticket_status_id;""", (cve, cpe, system, team)) 243 | data=cur.fetchone() 244 | c=list(data) if data else data 245 | 246 | if c: 247 | if c[10]: 248 | cur.execute("SELECT name FROM Ticket_Priorities where id=%s", (c[10],)) 249 | priority=(cur.fetchone())[0] 250 | else: 251 | priority=None 252 | cur.execute("SELECT name FROM Ticket_Resolutions") 253 | t_res= [x[0] for x in cur.fetchall()] 254 | cur.execute("SELECT name FROM Ticket_Priorities") 255 | t_prior= [x[0] for x in cur.fetchall()] 256 | cur.execute("SELECT name FROM Ticket_Urgencies") 257 | t_urgent= [x[0] for x in cur.fetchall()] 258 | cur.execute("""SELECT datetime, Users.user_name, cvss, ticket_notes, Ticket_Statusses.name, ticket_resolution_id, ticket_resolution_fix, 259 | ticket_priority_id, ticket_urgency_id 260 | FROM Ticket_Histories, Ticket_Statusses, Users 261 | WHERE ticket_id=%s 262 | AND Ticket_Statusses.id = Ticket_Histories.ticket_status_id 263 | AND Users.id = Ticket_Histories.user_id;""", (c[0],)) 264 | histories=cur.fetchall() 265 | h=[] 266 | for x in histories: 267 | h.append({"time": x[0], "user": x[1], "cvss": x[2], "notes": x[3], "status": x[4], "update": {"type": xFromy(x[5], t_res), "cpe": x[6]}, 268 | "priority": xFromy(x[7], t_prior), "urgency": xFromy(x[8], t_urgent)}) 269 | if c[7]: 270 | cur.execute("""SELECT name FROM Ticket_Resolutions WHERE id = %s""", (c[7],)) 271 | c[7]=cur.fetchone()[0] 272 | c=Ticket(c[1], c[2], c[3], c[4], status=c[5], notes=c[6], history=h, resolution=c[7], resolution_fix=c[8], cvss=c[9], priority=priority, objID=c[0]) 273 | else: 274 | tick=Ticket(team, system, cve, cpe) 275 | addTicket(tick) 276 | c=getTicket(tick.cve, tick.vulnComp["cpe"], tick.groupName, tick.team) 277 | c.addInfo(cvedb.getCVEInfo(cve)) 278 | updatePriority(c) 279 | return c 280 | 281 | 282 | @cursor_wrapped 283 | def getTickets(cur, team, system=None, limit=None): 284 | # Build up statement 285 | command = """SELECT Tickets.cve, Components.name, System_Groups.name, Teams.name 286 | FROM Tickets, System_Groups, Components, Teams, Ticket_histories 287 | WHERE System_Groups.id = Tickets.system_group_id 288 | AND Components.id = Tickets.component_id 289 | AND Teams.id = System_Groups.team_id 290 | AND Ticket_Histories.id = Tickets.history_last_id """ 291 | if limit and limit.lower()=="open": limit = """AND Ticket_Histories.ticket_status_id IN 292 | ( SELECT id FROM Ticket_Statusses WHERE name NOT LIKE 'closed-%%' ) """ 293 | elif limit and limit.lower()=="closed": limit = """AND Ticket_Histories.ticket_status_id IN 294 | ( SELECT id FROM Ticket_Statusses WHERE name LIKE 'closed-%%' ) """ 295 | else: limit = "" 296 | # Execute statement based on system or all systems 297 | if system: 298 | cur.execute(command + limit + """AND system_group_id IN ( SELECT id FROM System_Groups WHERE name = %s 299 | AND team_id IN ( SELECT id FROM Teams WHERE name = %s ))""", (system, team)) 300 | else: 301 | cur.execute(command + limit + """AND system_group_id IN ( SELECT id FROM System_Groups WHERE team_id 302 | IN ( SELECT id FROM Teams WHERE name = %s ))""", (team, )) 303 | return [getTicket(x[0],x[1],x[2],x[3]) for x in cur.fetchall()] 304 | 305 | 306 | @cursor_wrapped 307 | def getVulnsForSystem(cur, groupName, systemTeam, cpes=None): 308 | if type(cpes) is not list and cpes:cpes=[cpes] 309 | if not cpes: 310 | cur.execute("""SELECT name FROM Components WHERE id IN ( 311 | SELECT component_id FROM Components_in_System_Group WHERE system_group_id IN ( 312 | SELECT id FROM System_Groups WHERE name = %s AND team_id IN ( 313 | SELECT id FROM Teams WHERE name = %s )));""",(groupName, systemTeam)) 314 | cpes=[x[0] for x in cur.fetchall()] 315 | 316 | # Get tickets for every cve for the vulnerable systems 317 | tickets=[] 318 | for cpe in cpes: 319 | tickets.extend([getTicket(cve, cpe, groupName, systemTeam) for cve in [x["id"] for x in cvedb.getVulns([cpe], 0)]]) 320 | tickets=[x for x in tickets if not x.status.lower().startswith("closed")] 321 | return tickets 322 | 323 | 324 | @cursor_wrapped 325 | def getGroups(cur, team, group=None): 326 | teams=getTeamDicts() 327 | groups=[] 328 | query = """SELECT * FROM System_Groups WHERE team_id IN (SELECT id FROM Teams WHERE name=%s)""" 329 | if group: query+= "AND name=%s;" 330 | cur.execute(query, list(filter(None, [team, group]))) 331 | for g in cur.fetchall(): 332 | clist=[] 333 | cur.execute("""SELECT * FROM Components WHERE id IN ( 334 | SELECT component_ID FROM Components_in_System_Group WHERE system_group_id = %s );""", (g[0],)) 335 | for comp in cur.fetchall(): 336 | clist.append(Component(comp[1])) 337 | groups.append(SystemGroup(None, g[1], teams[g[2]], None, [], clist)) 338 | return groups 339 | 340 | 341 | @cursor_wrapped 342 | def getGroupDetails(cur, group): 343 | cur.execute("""SELECT name FROM Teams WHERE id=%s;""", (group[2],)) 344 | tid=cur.fetchone()[0] 345 | clist=[] 346 | cur.execute("""SELECT * FROM Components WHERE id IN ( SELECT component_ID FROM Components_in_System_Group WHERE system_group_id = %s );""", (group[0],)) 347 | for comp in cur.fetchall(): 348 | clist.append(Component(comp[1])) 349 | return SystemGroup(None, group[1], tid, None, [], clist) 350 | 351 | 352 | @cursor_wrapped 353 | def getTicketsForSystemComponent(cur, component, _system): 354 | tickets=getTickets(team, _system.groupName) 355 | #for x in tickets: 356 | # print(x) 357 | #tickets.append(getTicket(x[0], x[1], x[2], x[3])) 358 | return tickets 359 | 360 | # -- Get-IDs.py -- 361 | # Object ID's 362 | @cursor_wrapped 363 | def getSystemGroupID(cur, name, team): 364 | cur.execute("""SELECT System_Groups.id 365 | FROM System_Groups, Teams 366 | WHERE System_Groups.name = %s 367 | and Teams.name = %s 368 | and System_Groups.team_id = Teams.id;""", (name, team)) 369 | sid=cur.fetchone() 370 | return sid[0] if sid else None 371 | 372 | 373 | @cursor_wrapped 374 | def getComponentID(cur, name): 375 | cur.execute("""SELECT id FROM Components WHERE name = %s;""", (name, )) 376 | cid=cur.fetchone() 377 | return cid[0] if cid else None 378 | 379 | 380 | # List of ID's 381 | @cursor_wrapped 382 | def getPriorityLevels(cur): 383 | cur.execute("""SELECT id, min_cvss FROM Ticket_Priorities WHERE min_cvss IS NOT NULL;""") 384 | # Sort from highest CVSS to lowest 385 | priorities=sorted(cur.fetchall(),key=lambda x: x[1]) 386 | return priorities 387 | # -- Get-Dicts.py -- 388 | @cursor_wrapped 389 | def getLastTeamVulns(cur, team, limit): 390 | return cvedb.getVulns(getCPENames(team), limit) 391 | 392 | @cursor_wrapped 393 | def getGroupVulns(cur, team, colorVulnerable=False): 394 | c=getGroups(team) 395 | glist=[] 396 | for x in c: 397 | glist.append(x.getDict()) 398 | if colorVulnerable: 399 | for x in glist: 400 | for comp in x["components"]: 401 | vuln=cvedb.isVulnerable(comp["cpe"]) 402 | if vuln: x["vulnerable"]=True 403 | comp["vulnerable"]=vuln 404 | return sorted(glist, key=lambda s: s["groupName"].lower()) 405 | 406 | @cursor_wrapped 407 | def getStatistics(cur, team): 408 | today=date.today() 409 | start=date(today.year, today.month,1) 410 | if today.month < 12: end=date(today.year, today.month+1,1) 411 | else: end=date(today.year+1, 1, 1) 412 | systems=[] 413 | for x in getGroupVulns(team): 414 | new=0 415 | closed=0 416 | oac=0 417 | tickets=[] 418 | for cpe in [y["cpe"] for y in x["components"]]: 419 | tickets.extend([getTicket(cve, cpe, x["groupName"], team) for cve in [x["id"] for x in cvedb.getVulns([cpe], 0)]]) 420 | for ticket in tickets: 421 | opened=False 422 | for action in ticket.history: 423 | aDate=date(action["time"].year, action["time"].month, action["time"].day) 424 | if "status" in action and aDate >= start and aDate < end: 425 | if action["status"] == _DEFAULT_STATUS_TEXT: 426 | new+=1 427 | opened=True 428 | elif action["status"].lower().startswith("closed"): 429 | if opened: 430 | oac+=1 431 | new-=1 432 | else: 433 | closed+=1 434 | systems.append({"systems":x["groupName"], "new": new, "closed": closed, "openedAndClosed":oac}) 435 | return systems 436 | 437 | @cursor_wrapped 438 | def getTeamDicts(cur): 439 | cur.execute("SELECT id, name FROM Teams;") 440 | return {x[0]: x[1] for x in cur.fetchall()} 441 | 442 | @cursor_wrapped 443 | def getAccessRights(cur): 444 | cur.execute("SELECT * FROM Access_Rights;") 445 | return {v: k for k, v in dict(cur.fetchall()).items()} 446 | # -- Update.py -- 447 | @cursor_wrapped 448 | def setDBTicketStatus(cur, ticket, user, status=None, notes=None, update=None, updateType=None, ): 449 | #Allows us to work both with dict and Ticket types 450 | t=ticket.getDict() if type(ticket) == Ticket else ticket 451 | if type(ticket) == Ticket: t=getTicket(t["cve"], t["vulnComp"]["cpe"], t["groupName"], t["team"]).getDict() 452 | 453 | # Prevent unnecesarry database operations 454 | if all(None == i for i in [status, notes, update, updateType]): return False 455 | if status and t["status"] == status.strip(): return False 456 | if notes and t["notes"] == notes.strip(): return False 457 | if update and t["vulnComp"]["update"] == update.strip(): return False 458 | if updateType and t["vulnComp"]["type"] == updateType.strip(): return False 459 | # Fetch the current ticket ID and history 460 | cur.execute("""SELECT id, history_last_id FROM Tickets WHERE cve=%s 461 | AND component_id IN ( SELECT id FROM Components WHERE name = %s ) 462 | AND system_group_id IN ( SELECT id FROM System_Groups WHERE name = %s 463 | AND team_id IN ( SELECT id FROM Teams WHERE name = %s ));""", (t["cve"], t["vulnComp"]["cpe"], t["groupName"], t["team"])) 464 | x=cur.fetchone() 465 | if not x: return False 466 | t_id, h_id = x 467 | # Fetch all the ticket history information 468 | cur.execute("""SELECT * FROM Ticket_Histories WHERE id = %s;""", (h_id,)) 469 | h=cur.fetchone() 470 | # Get ID's of settings, or get the old id's 471 | if status == None: 472 | status=t["status"] 473 | cur.execute("""SELECT id FROM Ticket_Statusses WHERE name = %s;""", (status,)) 474 | status=cur.fetchone()[0] 475 | notes = t["notes"] if notes == None else notes 476 | update = t["vulnComp"]["update"] if update == None else update 477 | if updateType == None: updateType=t["vulnComp"]["type"] 478 | cur.execute("""SELECT id FROM Ticket_Resolutions WHERE name = %s;""", (updateType,)) 479 | updateType=cur.fetchone() 480 | updateType=updateType[0] if updateType else None 481 | # Get user ID 482 | cur.execute("""SELECT id FROM Users WHERE user_name = %s""", (user,)) 483 | uid=cur.fetchone()[0] 484 | # Now make a new entry using above data 485 | now = datetime.fromtimestamp(time.time()) 486 | cur.execute("""INSERT INTO Ticket_Histories VALUES(DEFAULT, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING id;""", 487 | (h[1], now, uid, h[4], h[5], notes, updateType, update, status, h[10], h[11] )) 488 | last_id=cur.fetchone()[0] 489 | cur.execute("""UPDATE Tickets SET history_last_id = %s WHERE id = %s;""", (last_id, t_id)) 490 | vmdb.commit() 491 | return True 492 | 493 | 494 | @cursor_wrapped 495 | def updateCPE(cur, _ticket): 496 | # temporary disable this function 497 | return 498 | # TODO: Verify if this structure won't cause Race conditions 499 | t=_ticket.getDict() 500 | update=t['vulnComp'] 501 | cid=addComponent(update['update']) 502 | sid=getSystemGroupID(t['groupName'], t['team']) 503 | ocid=getComponentID(update['cpe']) 504 | if update['type'] in ["upgrade", "merge"]: 505 | cur.execute("""UPDATE Components_in_System_Group 506 | SET component_id = %s 507 | WHERE component_id = %s 508 | and system_group_id = %s;""", (cid, ocid, sid)) 509 | vmdb.commit() 510 | elif update["type"] == "remove": 511 | cur.execute("""DELETE FROM Components_in_System_Group 512 | WHERE component_id = %s 513 | and system_group_id = %s;""", (ocid, sid)) 514 | vmdb.commit() 515 | # Else we assume patch, in which case nothing needs to happen, only return cpes. 516 | 517 | # Find list of related tickets 518 | return getTicketsForSystemComponent(update['cpe'], t['groupName'], t['team']) 519 | 520 | 521 | @cursor_wrapped 522 | def updatePriority(cur, _ticket): 523 | cvss=_ticket.cveInfo["cvss"] 524 | if _ticket.cvss == cvss: 525 | return 526 | priority=calculatePriority(ticket) 527 | #if priority 528 | # -- Calculations.py -- 529 | def calculatePriority(_ticket): 530 | priority=None 531 | try: 532 | cvss=float(_ticket.cveInfo["cvss"]) 533 | except: 534 | return None 535 | # TODO might to reverse loop to fix None problem 536 | for x in getPriorityLevels(): 537 | if x[1]4+x else None 33 | 34 | # get/set 35 | def getDict(self): 36 | return {"cpe":self.cpe, "vendor": self.vendor, "product": self.product, "version": self.version} 37 | -------------------------------------------------------------------------------- /lib/objects/SystemGroup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3.3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Vulnerability Manager SystemGroup object 5 | # 6 | # Copyright (c) 2014-2015 Pieter-Jan Moreels 7 | # 8 | # Software is free software released under the "Original BSD license" 9 | 10 | # Imports 11 | import os 12 | import sys 13 | _runPath = os.path.dirname(os.path.realpath(__file__)) 14 | sys.path.append(os.path.join(_runPath, "../..")) 15 | 16 | from lib.objects import Component 17 | from lib.exceptions import InvalidVariableTypes 18 | 19 | # Class 20 | class SystemGroup(): 21 | def verifyVars(self, groupID=None, groupName=None, team=None, notes=None, systems=None, components=None): 22 | if type(groupName) is not str or groupName.strip() is "": raise(InvalidVariableTypes) 23 | if not all(isinstance(x,str) for x in systems) or not all(isinstance(x,Component) for x in components): raise(InvalidVariableTypes) 24 | if notes is not None and type(notes) is not str: raise(InvalidVariableTypes) 25 | 26 | # init 27 | def __init__(self, groupID=None, groupName=None, team=None, notes=None, systems=None, components=None): 28 | self.verifyVars(groupID, groupName, team, notes, systems, components) 29 | self.groupID=groupID;self.groupName=groupName;self.team=team;self.notes=notes;self.components=components 30 | self.systems=[x for x in systems if x is not ""] 31 | 32 | # get/set 33 | def getDict(self): 34 | return {"groupID":self.groupID, "groupName": self.groupName, "team": self.team, 35 | "systems":self.systems, "components":[x.getDict() for x in self.components]} 36 | -------------------------------------------------------------------------------- /lib/objects/Ticket.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3.3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Vulnerability Manager SystemGroup object 5 | # 6 | # Copyright (c) 2014-2015 Pieter-Jan Moreels 7 | # 8 | # Software is free software released under the "Original BSD license" 9 | 10 | # Imports 11 | import os 12 | import sys 13 | _runPath = os.path.dirname(os.path.realpath(__file__)) 14 | sys.path.append(os.path.join(_runPath, "../..")) 15 | 16 | import decimal 17 | from datetime import datetime 18 | from lib.exceptions import InvalidVariableTypes 19 | 20 | # Object 21 | class Ticket(): 22 | def verifyVars(self, team, groupName, cve, cpe, status, notes, history, res, res_fix, user, cvss, priority, cveInfo, objID): 23 | if (type(status) is not str and status is not None or 24 | type(notes) is not str and notes is not None or 25 | type(res) is not str and res is not None or 26 | type(res_fix) is not str and res_fix is not None or 27 | type(history) is not list and history is not None or 28 | type(user) is not str and user is not None or 29 | type(priority) is not str and priority is not None or 30 | type(cvss) is not decimal.Decimal and cvss is not None or 31 | type(cveInfo) is not dict and cveInfo is not None or 32 | type(objID) is not int and objID is not None): raise(InvalidVariableTypes) 33 | if not all(isinstance(x,str) for x in [team, groupName, cve, cpe]): raise(InvalidVariableTypes) 34 | if history: 35 | if not all(isinstance(x,dict) for x in history): raise(InvalidVariableTypes) 36 | 37 | # init 38 | def __init__(self, team, groupName, cve, cpe, status=None, notes=None, history=None, resolution=None, resolution_fix=None, 39 | user=None, cvss=None, priority=None, cveInfo=None, objID=None): 40 | self.verifyVars(team, groupName, cve, cpe, status, notes, history, resolution, resolution_fix, user, cvss, priority, cveInfo, objID) 41 | self.team=team.strip(); self.groupName=groupName.strip(); self.cve=cve.strip(); self.cvss=cvss; self.cveInfo=cveInfo; self.objID=objID 42 | self.vulnComp={'cpe':cpe.strip(), 'update':resolution_fix, 'type':resolution} 43 | self.resolution=resolution.strip() if resolution else None; 44 | self.resolution_fix=resolution_fix.strip() if resolution_fix else None 45 | self.status=status.strip() if status else None 46 | self.notes=notes.strip() if notes else "" 47 | self.priority=priority.strip() if priority else "None" 48 | self.history=history if history else [{"time": datetime.now(), "status": self.status, "user":user}] 49 | 50 | def getDict(self): 51 | return {"team":self.team, "groupName":self.groupName, "cve":self.cve, "vulnComp":self.vulnComp, "status":self.status, 52 | "notes":self.notes, "history":self.history, "cvss":self.cvss, "priority": self.priority, "cve-info": self.cveInfo} 53 | 54 | def addInfo(self, info): 55 | if type(info) is not dict and info is not None: raise(InvalidVariableTypes) 56 | self.cveInfo = info 57 | -------------------------------------------------------------------------------- /lib/objects/User.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3.3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Vulnerability Manager SystemGroup object 5 | # 6 | # Copyright (c) 2014-2015 Pieter-Jan Moreels 7 | # 8 | # Software is free software released under the "Original BSD license" 9 | 10 | # Imports 11 | import os 12 | import sys 13 | _runPath = os.path.dirname(os.path.realpath(__file__)) 14 | sys.path.append(os.path.join(_runPath, "../..")) 15 | 16 | from flask.ext.login import UserMixin 17 | 18 | from lib.database.Users import getUser, checkPass 19 | from lib.exceptions import InvalidVariableTypes, UserNotFoundError 20 | 21 | # Object 22 | class User(UserMixin): 23 | def __init__(self, id): 24 | user=getUser(id) 25 | if not user: raise UserNotFoundError() 26 | self.id = id 27 | self.first_name=user["first_name"] 28 | self.last_name=user["last_name"] 29 | self.teams=user["teams"] 30 | 31 | @classmethod 32 | def get(self_class, id): 33 | try: 34 | return self_class(id) 35 | except UserNotFoundError: 36 | return None 37 | 38 | def verifyPass(self, pw): 39 | return True if checkPass(self.id, pw) else False 40 | -------------------------------------------------------------------------------- /lib/objects/__init__.py: -------------------------------------------------------------------------------- 1 | from .Component import Component 2 | from .SystemGroup import SystemGroup 3 | from .Ticket import Ticket 4 | from .User import User 5 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | psycopg2 2 | flask 3 | flask-login<=0.2.11 4 | -------------------------------------------------------------------------------- /sbin/static/css/style.css: -------------------------------------------------------------------------------- 1 | select, option { 2 | min-width:150px; 3 | } 4 | 5 | #status { 6 | position:fixed; 7 | top: 50%; 8 | left: 50%; 9 | width:50em; 10 | height:6em; 11 | margin-top: -3em; 12 | margin-left: -25em; 13 | border: 1px solid #ccc; 14 | text-align:center; 15 | z-index:99; 16 | } 17 | 18 | .hidden { 19 | visibility:hide; 20 | } 21 | 22 | #systemContent{ 23 | overflow-y:auto; 24 | max-height:90vh; 25 | } 26 | 27 | .clickable:hover{ 28 | cursor:pointer; 29 | } 30 | 31 | .no-link { 32 | color:inherit; 33 | text-decoration:none; 34 | cursor:pointer; 35 | } 36 | 37 | li.vulnerable { 38 | color: red; 39 | } 40 | 41 | span.vulnerable { 42 | color: red; 43 | } 44 | -------------------------------------------------------------------------------- /sbin/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NorthernSec/Vulnerability-management/5b4893428f0fbc451aed12ef46753ee97deec088/sbin/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /sbin/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NorthernSec/Vulnerability-management/5b4893428f0fbc451aed12ef46753ee97deec088/sbin/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /sbin/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NorthernSec/Vulnerability-management/5b4893428f0fbc451aed12ef46753ee97deec088/sbin/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /sbin/static/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NorthernSec/Vulnerability-management/5b4893428f0fbc451aed12ef46753ee97deec088/sbin/static/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /sbin/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NorthernSec/Vulnerability-management/5b4893428f0fbc451aed12ef46753ee97deec088/sbin/static/img/favicon.ico -------------------------------------------------------------------------------- /sbin/static/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.2 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.2",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.2",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.2",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a(this.options.trigger).filter('[href="#'+b.id+'"], [data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.2",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0,trigger:'[data-toggle="collapse"]'},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":a.extend({},e.data(),{trigger:this});c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.2",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('').insertAfter(a(this)).on("click",b);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(b){if(/(38|40|27|32)/.test(b.which)&&!/input|textarea/i.test(b.target.tagName)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var e=c(d),g=e.hasClass("open");if(!g&&27!=b.which||g&&27==b.which)return 27==b.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.divider):visible a",i=e.find('[role="menu"]'+h+', [role="listbox"]'+h);if(i.length){var j=i.index(b.target);38==b.which&&j>0&&j--,40==b.which&&j').prependTo(this.$element).on("click.dismiss.bs.modal",a.proxy(function(a){a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus.call(this.$element[0]):this.hide.call(this))},this)),f&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;f?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var g=function(){d.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):g()}else b&&b()},c.prototype.handleUpdate=function(){this.options.backdrop&&this.adjustBackdrop(),this.adjustDialog()},c.prototype.adjustBackdrop=function(){this.$backdrop.css("height",0).css("height",this.$element[0].scrollHeight)},c.prototype.adjustDialog=function(){var a=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){this.bodyIsOverflowing=document.body.scrollHeight>document.documentElement.clientHeight,this.scrollbarWidth=this.measureScrollbar()},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.bodyIsOverflowing&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right","")},c.prototype.measureScrollbar=function(){var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b;(e||"destroy"!=b)&&(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=this.options=this.enabled=this.timeout=this.hoverState=this.$element=null,this.init("tooltip",a,b)};c.VERSION="3.3.2",c.TRANSITION_DURATION=150,c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(this.options.viewport.selector||this.options.viewport);for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c&&c.$tip&&c.$tip.is(":visible")?void(c.hoverState="in"):(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.options.container?a(this.options.container):this.$element.parent(),p=this.getPosition(o);h="bottom"==h&&k.bottom+m>p.bottom?"top":"top"==h&&k.top-mp.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.width&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){return this.$tip=this.$tip||a(this.options.template)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type)})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||"destroy"!=b)&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.2",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},c.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){var e=a.proxy(this.process,this);this.$body=a("body"),this.$scrollElement=a(a(c).is("body")?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",e),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.2",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b="offset",c=0;a.isWindow(this.$scrollElement[0])||(b="position",c=this.$scrollElement.scrollTop()),this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight();var d=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+c,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){d.offsets.push(this[0]),d.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,this.clear();var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")},b.prototype.clear=function(){a(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.3.2",c.TRANSITION_DURATION=150,c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a"),f=a.Event("hide.bs.tab",{relatedTarget:b[0]}),g=a.Event("show.bs.tab",{relatedTarget:e[0]});if(e.trigger(f),b.trigger(g),!g.isDefaultPrevented()&&!f.isDefaultPrevented()){var h=a(d);this.activate(b.closest("li"),c),this.activate(h,h.parent(),function(){e.trigger({type:"hidden.bs.tab",relatedTarget:b[0]}),b.trigger({type:"shown.bs.tab",relatedTarget:e[0]})})}}},c.prototype.activate=function(b,d,e){function f(){g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e() 7 | }var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.2",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=a("body").height();"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); -------------------------------------------------------------------------------- /sbin/static/js/custom/components.js: -------------------------------------------------------------------------------- 1 | // Add 2 | $(function() { 3 | $('#add').bind('click', function() { 4 | $.getJSON('/_add_component', { 5 | name: $('#name').val(), 6 | cpe: $('#cpe').val(), 7 | comments: $('#comments').val() 8 | }, function(data) { 9 | var option = ''; 10 | for (i=0;i' + data["components"][i]["name"] + ''; 12 | } 13 | $('#components').empty().append(option); 14 | }); 15 | $("#name").val(""); 16 | $("#cpe").val(""); 17 | $("#comments").val(""); 18 | return false; 19 | }); 20 | }); 21 | 22 | // Load 23 | $(function() { 24 | $('#load').bind('click', function() { 25 | $.getJSON('/_get_component', { 26 | cpe: $('#components').val(), 27 | }, function(data) { 28 | $("#name").val(data["name"]); 29 | $("#cpe").val(data["cpe"]); 30 | $("#comments").val(data["comments"].join("\n")); 31 | }); 32 | return false; 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /sbin/static/js/custom/scripts.js: -------------------------------------------------------------------------------- 1 | function alphasort(list){ 2 | var opts = $(list+" option"); 3 | opts.sort(function(a,b) { 4 | if (a.text.toLowerCase() > b.text.toLowerCase()) return 1; 5 | else if (a.text.toLowerCase() < b.text.toLowerCase()) return -1; 6 | else return 0 7 | }) 8 | $(list).empty().append(opts); 9 | } 10 | 11 | function loading(target, page, query){ 12 | $("body").css("cursor", "progress"); 13 | $(target).load(page, query, function(){$("body").css("cursor", "default");}); 14 | } 15 | -------------------------------------------------------------------------------- /sbin/static/js/custom/status.js: -------------------------------------------------------------------------------- 1 | function showStatus(data){ 2 | $("#status").removeClass(); 3 | $("#status_icon").removeClass(); 4 | if(data['status'][1] =='success'){ 5 | $("#status").addClass("alert alert-success"); 6 | $("#status_icon").addClass("glyphicon glyphicon-ok-sign"); 7 | }else if (data['status'][1] =='info'){ 8 | $("#status").addClass("alert alert-info"); 9 | $("#status_icon").addClass("glyphicon glyphicon-info-sign"); 10 | }else if (data['status'][1] =='warning'){ 11 | $("#status").addClass("alert alert-warning"); 12 | $("#status_icon").addClass("glyphicon glyphicon-warning-sign"); 13 | }else if (data['status'][1] =='error'){ 14 | $("#status").addClass("alert alert-danger"); 15 | $("#status_icon").addClass("glyphicon glyphicon-remove-sign"); 16 | } 17 | $("#status_message").empty(); 18 | if( data['status'][0] == 'system_added' ){ $("#status_message").append("The system group has been added");} 19 | else if(data['status'][0] == 'system_removed' ){ $("#status_message").append("The system group has been removed");} 20 | else if(data['status'][0] == 'system_updated' ){ $("#status_message").append("The system group has been updated");} 21 | else if(data['status'][0] == 'action_failed' ){ $("#status_message").append("The following action could not be completed: "+data["error"]);} 22 | else if(data['status'][0] == 'update_failed' ){ $("#status_message").append("");} 23 | var duration = 5000; 24 | if(data['status'][1] =='success'){duration = 3000}; 25 | $("#status").removeTemporaryClass("hidden", duration); 26 | } 27 | 28 | //Temporary undo class 29 | (function($){ 30 | $.fn.extend({ 31 | removeTemporaryClass: function(className, duration) { 32 | var elements = this; 33 | setTimeout(function() { 34 | elements.addClass(className); 35 | }, duration); 36 | return this.each(function() { 37 | $(this).removeClass(className); 38 | }); 39 | } 40 | }); 41 | })(jQuery); 42 | -------------------------------------------------------------------------------- /sbin/static/js/highcharts/exporting.js: -------------------------------------------------------------------------------- 1 | /* 2 | Highcharts JS v4.1.7 (2015-06-26) 3 | Exporting module 4 | 5 | (c) 2010-2014 Torstein Honsi 6 | 7 | License: www.highcharts.com/license 8 | */ 9 | (function(f){var z=f.Chart,s=f.addEvent,A=f.removeEvent,B=HighchartsAdapter.fireEvent,j=f.createElement,p=f.discardElement,u=f.css,l=f.merge,m=f.each,q=f.extend,E=f.splat,F=Math.max,k=document,C=window,G=f.isTouchDevice,H=f.Renderer.prototype.symbols,r=f.getOptions(),x;q(r.lang,{printChart:"Print chart",downloadPNG:"Download PNG image",downloadJPEG:"Download JPEG image",downloadPDF:"Download PDF document",downloadSVG:"Download SVG vector image",contextButtonTitle:"Chart context menu"});r.navigation= 10 | {menuStyle:{border:"1px solid #A0A0A0",background:"#FFFFFF",padding:"5px 0"},menuItemStyle:{padding:"0 10px",background:"none",color:"#303030",fontSize:G?"14px":"11px"},menuItemHoverStyle:{background:"#4572A5",color:"#FFFFFF"},buttonOptions:{symbolFill:"#E0E0E0",symbolSize:14,symbolStroke:"#666",symbolStrokeWidth:3,symbolX:12.5,symbolY:10.5,align:"right",buttonSpacing:3,height:22,theme:{fill:"white",stroke:"none"},verticalAlign:"top",width:24}};r.exporting={type:"image/png",url:"http://export.highcharts.com/", 11 | buttons:{contextButton:{menuClassName:"highcharts-contextmenu",symbol:"menu",_titleKey:"contextButtonTitle",menuItems:[{textKey:"printChart",onclick:function(){this.print()}},{separator:!0},{textKey:"downloadPNG",onclick:function(){this.exportChart()}},{textKey:"downloadJPEG",onclick:function(){this.exportChart({type:"image/jpeg"})}},{textKey:"downloadPDF",onclick:function(){this.exportChart({type:"application/pdf"})}},{textKey:"downloadSVG",onclick:function(){this.exportChart({type:"image/svg+xml"})}}]}}}; 12 | f.post=function(b,a,e){var c,b=j("form",l({method:"post",action:b,enctype:"multipart/form-data"},e),{display:"none"},k.body);for(c in a)j("input",{type:"hidden",name:c,value:a[c]},null,b);b.submit();p(b)};q(z.prototype,{sanitizeSVG:function(b){return b.replace(/zIndex="[^"]+"/g,"").replace(/isShadow="[^"]+"/g,"").replace(/symbolName="[^"]+"/g,"").replace(/jQuery[0-9]+="[^"]+"/g,"").replace(/url\([^#]+#/g,"url(#").replace(/.*?$/,"").replace(/(fill|stroke)="rgba\(([ 0-9]+,[ 0-9]+,[ 0-9]+),([ 0-9\.]+)\)"/g,'$1="rgb($2)" $1-opacity="$3"').replace(/ /g," ").replace(//g,"").replace(//g,"<$1title>").replace(/height=([^" ]+)/g,'height="$1"').replace(/width=([^" ]+)/g,'width="$1"').replace(/hc-svg-href="([^"]+)">/g,'xlink:href="$1"/>').replace(/ id=([^" >]+)/g,' id="$1"').replace(/class=([^" >]+)/g,'class="$1"').replace(/ transform /g, 14 | " ").replace(/:(path|rect)/g,"$1").replace(/style="([^"]+)"/g,function(a){return a.toLowerCase()})},getSVG:function(b){var a=this,e,c,g,y,h,d=l(a.options,b);if(!k.createElementNS)k.createElementNS=function(a,b){return k.createElement(b)};c=j("div",null,{position:"absolute",top:"-9999em",width:a.chartWidth+"px",height:a.chartHeight+"px"},k.body);g=a.renderTo.style.width;h=a.renderTo.style.height;g=d.exporting.sourceWidth||d.chart.width||/px$/.test(g)&&parseInt(g,10)||600;h=d.exporting.sourceHeight|| 15 | d.chart.height||/px$/.test(h)&&parseInt(h,10)||400;q(d.chart,{animation:!1,renderTo:c,forExport:!0,width:g,height:h});d.exporting.enabled=!1;delete d.data;d.series=[];m(a.series,function(a){y=l(a.options,{animation:!1,enableMouseTracking:!1,showCheckbox:!1,visible:a.visible});y.isInternal||d.series.push(y)});b&&m(["xAxis","yAxis"],function(a){m(E(b[a]),function(b,c){d[a][c]=l(d[a][c],b)})});e=new f.Chart(d,a.callback);m(["xAxis","yAxis"],function(b){m(a[b],function(a,d){var c=e[b][d],g=a.getExtremes(), 16 | h=g.userMin,g=g.userMax;c&&(h!==void 0||g!==void 0)&&c.setExtremes(h,g,!0,!1)})});g=e.container.innerHTML;d=null;e.destroy();p(c);g=this.sanitizeSVG(g);return g=g.replace(/(url\(#highcharts-[0-9]+)"/g,"$1").replace(/"/g,"'")},getSVGForExport:function(b,a){var e=this.options.exporting;return this.getSVG(l({chart:{borderRadius:0}},e.chartOptions,a,{exporting:{sourceWidth:b&&b.sourceWidth||e.sourceWidth,sourceHeight:b&&b.sourceHeight||e.sourceHeight}}))},exportChart:function(b,a){var e=this.getSVGForExport(b, 17 | a),b=l(this.options.exporting,b);f.post(b.url,{filename:b.filename||"chart",type:b.type,width:b.width||0,scale:b.scale||2,svg:e},b.formAttributes)},print:function(){var b=this,a=b.container,e=[],c=a.parentNode,g=k.body,f=g.childNodes;if(!b.isPrinting)b.isPrinting=!0,B(b,"beforePrint"),m(f,function(a,b){if(a.nodeType===1)e[b]=a.style.display,a.style.display="none"}),g.appendChild(a),C.focus(),C.print(),setTimeout(function(){c.appendChild(a);m(f,function(a,b){if(a.nodeType===1)a.style.display=e[b]}); 18 | b.isPrinting=!1;B(b,"afterPrint")},1E3)},contextMenu:function(b,a,e,c,g,f,h){var d=this,l=d.options.navigation,D=l.menuItemStyle,n=d.chartWidth,o=d.chartHeight,k="cache-"+b,i=d[k],t=F(g,f),v,w,p,r=function(a){d.pointer.inClass(a.target,b)||w()};if(!i)d[k]=i=j("div",{className:b},{position:"absolute",zIndex:1E3,padding:t+"px"},d.container),v=j("div",null,q({MozBoxShadow:"3px 3px 10px #888",WebkitBoxShadow:"3px 3px 10px #888",boxShadow:"3px 3px 10px #888"},l.menuStyle),i),w=function(){u(i,{display:"none"}); 19 | h&&h.setState(0);d.openMenu=!1},s(i,"mouseleave",function(){p=setTimeout(w,500)}),s(i,"mouseenter",function(){clearTimeout(p)}),s(document,"mouseup",r),s(d,"destroy",function(){A(document,"mouseup",r)}),m(a,function(a){if(a){var b=a.separator?j("hr",null,null,v):j("div",{onmouseover:function(){u(this,l.menuItemHoverStyle)},onmouseout:function(){u(this,D)},onclick:function(){w();a.onclick&&a.onclick.apply(d,arguments)},innerHTML:a.text||d.options.lang[a.textKey]},q({cursor:"pointer"},D),v);d.exportDivElements.push(b)}}), 20 | d.exportDivElements.push(v,i),d.exportMenuWidth=i.offsetWidth,d.exportMenuHeight=i.offsetHeight;a={display:"block"};e+d.exportMenuWidth>n?a.right=n-e-g-t+"px":a.left=e-t+"px";c+f+d.exportMenuHeight>o&&h.alignOptions.verticalAlign!=="top"?a.bottom=o-c-t+"px":a.top=c+f-t+"px";u(i,a);d.openMenu=!0},addButton:function(b){var a=this,e=a.renderer,c=l(a.options.navigation.buttonOptions,b),g=c.onclick,k=c.menuItems,h,d,m={stroke:c.symbolStroke,fill:c.symbolFill},j=c.symbolSize||12;if(!a.btnCount)a.btnCount= 21 | 0;if(!a.exportDivElements)a.exportDivElements=[],a.exportSVGElements=[];if(c.enabled!==!1){var n=c.theme,o=n.states,p=o&&o.hover,o=o&&o.select,i;delete n.states;g?i=function(){g.apply(a,arguments)}:k&&(i=function(){a.contextMenu(d.menuClassName,k,d.translateX,d.translateY,d.width,d.height,d);d.setState(2)});c.text&&c.symbol?n.paddingLeft=f.pick(n.paddingLeft,25):c.text||q(n,{width:c.width,height:c.height,padding:0});d=e.button(c.text,0,0,i,n,p,o).attr({title:a.options.lang[c._titleKey],"stroke-linecap":"round"}); 22 | d.menuClassName=b.menuClassName||"highcharts-menu-"+a.btnCount++;c.symbol&&(h=e.symbol(c.symbol,c.symbolX-j/2,c.symbolY-j/2,j,j).attr(q(m,{"stroke-width":c.symbolStrokeWidth||1,zIndex:1})).add(d));d.add().align(q(c,{width:d.width,x:f.pick(c.x,x)}),!0,"spacingBox");x+=(d.width+c.buttonSpacing)*(c.align==="right"?-1:1);a.exportSVGElements.push(d,h)}},destroyExport:function(b){var b=b.target,a,e;for(a=0;am.len*m.tickInterval/(m.max-m.min)&&(n=0),h=A>n&&A<180-n?"left":A>180+n&&A<360-n?"right":"center"):h="center",d.attr({align:h})),a.x+=f.x,a.y+=j):a=a.call(this,b,c,d,e,f,i,g,l);return a});u(y,"getMarkPath",function(a,b,c,d,e,f,i){var g=this.axis;g.isRadial?(a=g.getPosition(this.pos,g.center[2]/2+d),b= 20 | ["M",b,c,"L",a.x,a.y]):b=a.call(this,b,c,d,e,f,i);return b});p.arearange=o(p.area,{lineWidth:1,marker:null,threshold:null,tooltip:{pointFormat:'\u25CF {series.name}: {point.low} - {point.high}'},trackByArea:!0,dataLabels:{align:null,verticalAlign:null,xLow:0,xHigh:0,yLow:0,yHigh:0},states:{hover:{halo:!1}}});h.arearange=v(h.area,{type:"arearange",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"low",deferTranslatePolar:!0, 21 | highToXY:function(a){var b=this.chart,c=this.xAxis.postTranslate(a.rectPlotX,this.yAxis.len-a.plotHigh);a.plotHighX=c.x-b.plotLeft;a.plotHigh=c.y-b.plotTop},getSegments:function(){var a=this;t(a.points,function(b){if(!a.options.connectNulls&&(b.low===null||b.high===null))b.y=null;else if(b.low===null&&b.high!==null)b.y=b.high});s.prototype.getSegments.call(this)},translate:function(){var a=this,b=a.yAxis;h.area.prototype.translate.apply(a);t(a.points,function(a){var d=a.low,e=a.high,f=a.plotY;e=== 22 | null&&d===null?a.y=null:d===null?(a.plotLow=a.plotY=null,a.plotHigh=b.translate(e,0,1,0,1)):e===null?(a.plotLow=f,a.plotHigh=null):(a.plotLow=f,a.plotHigh=b.translate(e,0,1,0,1))});this.chart.polar&&t(this.points,function(c){a.highToXY(c)})},getSegmentPath:function(a){var b,c=[],d=a.length,e=s.prototype.getSegmentPath,f,i;i=this.options;var g=i.step;for(b=HighchartsAdapter.grep(a,function(a){return a.plotLow!==null});d--;)f=a[d],f.plotHigh!==null&&c.push({plotX:f.plotHighX||f.plotX,plotY:f.plotHigh}); 23 | a=e.call(this,b);if(g)g===!0&&(g="left"),i.step={left:"right",center:"center",right:"left"}[g];c=e.call(this,c);i.step=g;i=[].concat(a,c);this.chart.polar||(c[0]="L");this.areaPath=this.areaPath.concat(a,c);return i},drawDataLabels:function(){var a=this.data,b=a.length,c,d=[],e=s.prototype,f=this.options.dataLabels,i=f.align,g,l,m=this.chart.inverted;if(f.enabled||this._hasPointLabels){for(c=b;c--;)if(g=a[c])if(l=g.plotHigh>g.plotLow,g.y=g.high,g._plotY=g.plotY,g.plotY=g.plotHigh,d[c]=g.dataLabel, 24 | g.dataLabel=g.dataLabelUpper,g.below=l,m){if(!i)f.align=l?"right":"left";f.x=f.xHigh}else f.y=f.yHigh;e.drawDataLabels&&e.drawDataLabels.apply(this,arguments);for(c=b;c--;)if(g=a[c])if(l=g.plotHigh>g.plotLow,g.dataLabelUpper=g.dataLabel,g.dataLabel=d[c],g.y=g.low,g.plotY=g._plotY,g.below=!l,m){if(!i)f.align=l?"left":"right";f.x=f.xLow}else f.y=f.yLow;e.drawDataLabels&&e.drawDataLabels.apply(this,arguments)}f.align=i},alignDataLabel:function(){h.column.prototype.alignDataLabel.apply(this,arguments)}, 25 | setStackedPoints:r,getSymbol:r,drawPoints:r});p.areasplinerange=o(p.arearange);h.areasplinerange=v(h.arearange,{type:"areasplinerange",getPointSpline:h.spline.prototype.getPointSpline});(function(){var a=h.column.prototype;p.columnrange=o(p.column,p.arearange,{lineWidth:1,pointRange:null});h.columnrange=v(h.arearange,{type:"columnrange",translate:function(){var b=this,c=b.yAxis,d;a.translate.apply(b);t(b.points,function(a){var f=a.shapeArgs,i=b.options.minPointLength,g;a.tooltipPos=null;a.plotHigh= 26 | d=c.translate(a.high,0,1,0,1);a.plotLow=a.plotY;g=d;a=a.plotY-d;Math.abs(a)\u25CF {series.name}Maximum: {point.high}Upper quartile: {point.q3}Median: {point.median}Lower quartile: {point.q1}Minimum: {point.low}'},whiskerLength:"50%",whiskerWidth:2});h.boxplot=v(h.column,{type:"boxplot",pointArrayMap:["low","q1", 32 | "median","q3","high"],toYData:function(a){return[a.low,a.q1,a.median,a.q3,a.high]},pointValKey:"high",pointAttrToOptions:{fill:"fillColor",stroke:"color","stroke-width":"lineWidth"},drawDataLabels:r,translate:function(){var a=this.yAxis,b=this.pointArrayMap;h.column.prototype.translate.apply(this);t(this.points,function(c){t(b,function(b){c[b]!==null&&(c[b+"Plot"]=a.translate(c[b],0,1,0,1))})})},drawPoints:function(){var a=this,b=a.points,c=a.options,d=a.chart.renderer,e,f,i,g,l,m,j,n,h,A,k,J,p,o, 33 | u,r,v,s,w,x,z,y,F=a.doQuartiles!==!1,C=parseInt(a.options.whiskerLength,10)/100;t(b,function(b){h=b.graphic;z=b.shapeArgs;k={};o={};r={};y=b.color||a.color;if(b.plotY!==D)if(e=b.pointAttr[b.selected?"selected":""],v=z.width,s=B(z.x),w=s+v,x=E(v/2),f=B(F?b.q1Plot:b.lowPlot),i=B(F?b.q3Plot:b.lowPlot),g=B(b.highPlot),l=B(b.lowPlot),k.stroke=b.stemColor||c.stemColor||y,k["stroke-width"]=q(b.stemWidth,c.stemWidth,c.lineWidth),k.dashstyle=b.stemDashStyle||c.stemDashStyle,o.stroke=b.whiskerColor||c.whiskerColor|| 34 | y,o["stroke-width"]=q(b.whiskerWidth,c.whiskerWidth,c.lineWidth),r.stroke=b.medianColor||c.medianColor||y,r["stroke-width"]=q(b.medianWidth,c.medianWidth,c.lineWidth),j=k["stroke-width"]%2/2,n=s+x+j,A=["M",n,i,"L",n,g,"M",n,f,"L",n,l],F&&(j=e["stroke-width"]%2/2,n=B(n)+j,f=B(f)+j,i=B(i)+j,s+=j,w+=j,J=["M",s,i,"L",s,f,"L",w,f,"L",w,i,"L",s,i,"z"]),C&&(j=o["stroke-width"]%2/2,g+=j,l+=j,p=["M",n-x*C,g,"L",n+x*C,g,"M",n-x*C,l,"L",n+x*C,l]),j=r["stroke-width"]%2/2,m=E(b.medianPlot)+j,u=["M",s,m,"L",w, 35 | m],h)b.stem.animate({d:A}),C&&b.whiskers.animate({d:p}),F&&b.box.animate({d:J}),b.medianShape.animate({d:u});else{b.graphic=h=d.g().add(a.group);b.stem=d.path(A).attr(k).add(h);if(C)b.whiskers=d.path(p).attr(o).add(h);if(F)b.box=d.path(J).attr(e).add(h);b.medianShape=d.path(u).attr(r).add(h)}})},setStackedPoints:r});p.errorbar=o(p.boxplot,{color:"#000000",grouping:!1,linkedTo:":previous",tooltip:{pointFormat:'\u25CF {series.name}: {point.low} - {point.high}'}, 36 | whiskerWidth:null});h.errorbar=v(h.boxplot,{type:"errorbar",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"high",doQuartiles:!1,drawDataLabels:h.arearange?h.arearange.prototype.drawDataLabels:r,getColumnMetrics:function(){return this.linkedParent&&this.linkedParent.columnMetrics||h.column.prototype.getColumnMetrics.call(this)}});p.waterfall=o(p.column,{lineWidth:1,lineColor:"#333",dashStyle:"dot",borderColor:"#333",dataLabels:{inside:!0},states:{hover:{lineWidthPlus:0}}}); 37 | h.waterfall=v(h.column,{type:"waterfall",upColorProp:"fill",pointValKey:"y",translate:function(){var a=this.options,b=this.yAxis,c,d,e,f,i,g,l,m,j,n=a.threshold,k=a.stacking;h.column.prototype.translate.apply(this);l=m=n;d=this.points;for(c=0,a=d.length;c0?b.translate(l,0,1)-f.y:b.translate(l,0,1)-b.translate(l-g,0,1);l+=g}f.height<0&&(f.y+=f.height,f.height*=-1);e.plotY=f.y=E(f.y)-this.borderWidth%2/2;f.height=N(E(f.height),0.001);e.yBottom=f.y+f.height;f=e.plotY+(e.negative?f.height:0);this.chart.inverted?e.tooltipPos[0]=b.len- 39 | f:e.tooltipPos[1]=f}},processData:function(a){var b=this.yData,c=this.options.data,d,e=b.length,f,i,g,l,m,j;i=f=g=l=this.options.threshold||0;for(j=0;j0?(b.pointAttr=e,b.color=d):b.pointAttr=a.pointAttr})},getGraphPath:function(){var a=this.data,b=a.length,c=E(this.options.lineWidth+this.borderWidth)%2/2,d=[],e,f,i;for(i=1;i0?(g[f]-a)/(b-a):0.5,m&&i>=0&&(i=Math.sqrt(i)),l.push(w.ceil(c+i*(d-c))/2);this.radii=l},animate:function(a){var b=this.options.animation;if(!a)t(this.points,function(a){var d=a.graphic,a=a.shapeArgs;d&&a&&(d.attr("r",1),d.animate({r:a.r},b))}),this.animate=null},translate:function(){var a,b=this.data,c,d,e=this.radii;h.scatter.prototype.translate.call(this);for(a=b.length;a--;)c=b[a],d=e?e[a]:0,d>=this.minPxSize/2?(c.shapeType="circle",c.shapeArgs={x:c.plotX,y:c.plotY,r:d},c.dlBox= 45 | {x:c.plotX-d,y:c.plotY-d,width:2*d,height:2*d}):c.shapeArgs=c.plotY=c.dlBox=D},drawLegendSymbol:function(a,b){var c=x(a.itemStyle.fontSize)/2;b.legendSymbol=this.chart.renderer.circle(c,a.baseline-c,c).attr({zIndex:3}).add(b.legendGroup);b.legendSymbol.isMarker=!0},drawPoints:h.column.prototype.drawPoints,alignDataLabel:h.column.prototype.alignDataLabel,buildKDTree:r,applyZones:r});M.prototype.beforePadding=function(){var a=this,b=this.len,c=this.chart,d=0,e=b,f=this.isXAxis,i=f?"xData":"yData",g= 46 | this.min,l={},m=w.min(c.plotWidth,c.plotHeight),j=Number.MAX_VALUE,n=-Number.MAX_VALUE,h=this.max-g,k=b/h,p=[];t(this.series,function(b){var g=b.options;if(b.bubblePadding&&(b.visible||!c.options.chart.ignoreHiddenSeries))if(a.allowZoomOutside=!0,p.push(b),f)t(["minSize","maxSize"],function(a){var b=g[a],f=/%$/.test(b),b=x(b);l[a]=f?m*b/100:b}),b.minPxSize=l.minSize,b=b.zData,b.length&&(j=q(g.zMin,w.min(j,w.max(P(b),g.displayNegative===!1?g.zThreshold:-Number.MAX_VALUE))),n=q(g.zMax,w.max(n,Q(b))))}); 47 | t(p,function(a){var b=a[i],c=b.length,m;f&&a.getRadii(j,n,l.minSize,l.maxSize);if(h>0)for(;c--;)typeof b[c]==="number"&&(m=a.radii[c],d=Math.min((b[c]-g)*k-m,d),e=Math.max((b[c]-g)*k+m,e))});p.length&&h>0&&q(this.options.min,this.userMin)===D&&q(this.options.max,this.userMax)===D&&(e-=b,k*=(b+d-e)/b,this.min+=d/k,this.max+=e/k)};(function(){function a(a,b,c){a.call(this,b,c);if(this.chart.polar)this.closeSegment=function(a){var b=this.xAxis.center;a.push("L",b[0],b[1])},this.closedStacks=!0}function b(a, 48 | b){var c=this.chart,d=this.options.animation,e=this.group,j=this.markerGroup,n=this.xAxis.center,h=c.plotLeft,k=c.plotTop;if(c.polar){if(c.renderer.isSVG)d===!0&&(d={}),b?(c={translateX:n[0]+h,translateY:n[1]+k,scaleX:0.001,scaleY:0.001},e.attr(c),j&&j.attr(c)):(c={translateX:h,translateY:k,scaleX:1,scaleY:1},e.animate(c,d),j&&j.animate(c,d),this.animate=null)}else a.call(this,b)}var c=s.prototype,d=S.prototype,e;c.searchPointByAngle=function(a){var b=this.chart,c=this.xAxis.pane.center;return this.searchKDTree({clientX:180+ 49 | Math.atan2(a.chartX-c[0]-b.plotLeft,a.chartY-c[1]-b.plotTop)*(-180/Math.PI)})};u(c,"buildKDTree",function(a){if(this.chart.polar)this.kdByAngle?this.searchPoint=this.searchPointByAngle:this.kdDimensions=2;a.apply(this)});c.toXY=function(a){var b,c=this.chart,d=a.plotX;b=a.plotY;a.rectPlotX=d;a.rectPlotY=b;b=this.xAxis.postTranslate(a.plotX,this.yAxis.len-b);a.plotX=a.polarPlotX=b.x-c.plotLeft;a.plotY=a.polarPlotY=b.y-c.plotTop;this.kdByAngle?(c=(d/Math.PI*180+this.xAxis.pane.options.startAngle)%360, 50 | c<0&&(c+=360),a.clientX=c):a.clientX=a.plotX};h.area&&u(h.area.prototype,"init",a);h.areaspline&&u(h.areaspline.prototype,"init",a);h.spline&&u(h.spline.prototype,"getPointSpline",function(a,b,c,d){var e,j,n,h,k,p,o;if(this.chart.polar){e=c.plotX;j=c.plotY;a=b[d-1];n=b[d+1];this.connectEnds&&(a||(a=b[b.length-2]),n||(n=b[1]));if(a&&n)h=a.plotX,k=a.plotY,b=n.plotX,p=n.plotY,h=(1.5*e+h)/2.5,k=(1.5*j+k)/2.5,n=(1.5*e+b)/2.5,o=(1.5*j+p)/2.5,b=Math.sqrt(Math.pow(h-e,2)+Math.pow(k-j,2)),p=Math.sqrt(Math.pow(n- 51 | e,2)+Math.pow(o-j,2)),h=Math.atan2(k-j,h-e),k=Math.atan2(o-j,n-e),o=Math.PI/2+(h+k)/2,Math.abs(h-o)>Math.PI/2&&(o-=Math.PI),h=e+Math.cos(o)*b,k=j+Math.sin(o)*b,n=e+Math.cos(Math.PI+o)*p,o=j+Math.sin(Math.PI+o)*p,c.rightContX=n,c.rightContY=o;d?(c=["C",a.rightContX||a.plotX,a.rightContY||a.plotY,h||e,k||j,e,j],a.rightContX=a.rightContY=null):c=["M",e,j]}else c=a.call(this,b,c,d);return c});u(c,"translate",function(a){var b=this.chart;a.call(this);if(b.polar&&(this.kdByAngle=b.tooltip&&b.tooltip.shared, 52 | !this.preventPostTranslate)){a=this.points;for(b=a.length;b--;)this.toXY(a[b])}});u(c,"getSegmentPath",function(a,b){var c=this.points;if(this.chart.polar&&this.options.connectEnds!==!1&&b[b.length-1]===c[c.length-1]&&c[0].y!==null)this.connectEnds=!0,b=[].concat(b,[c[0]]);return a.call(this,b)});u(c,"animate",b);if(h.column)e=h.column.prototype,u(e,"animate",b),u(e,"translate",function(a){var b=this.xAxis,c=this.yAxis.len,d=b.center,e=b.startAngleRad,j=this.chart.renderer,h,k;this.preventPostTranslate= 53 | !0;a.call(this);if(b.isRadial){b=this.points;for(k=b.length;k--;)h=b[k],a=h.barX+e,h.shapeType="path",h.shapeArgs={d:j.symbols.arc(d[0],d[1],c-h.plotY,null,{start:a,end:a+h.pointWidth,innerR:c-q(h.yBottom,c)})},this.toXY(h),h.tooltipPos=[h.plotX,h.plotY],h.ttBelow=h.plotY>d[1]}}),u(e,"alignDataLabel",function(a,b,d,e,h,j){if(this.chart.polar){a=b.rectPlotX/Math.PI*180;if(e.align===null)e.align=a>20&&a<160?"left":a>200&&a<340?"right":"center";if(e.verticalAlign===null)e.verticalAlign=a<45||a>315?"bottom": 54 | a>135&&a<225?"top":"middle";c.alignDataLabel.call(this,b,d,e,h,j)}else a.call(this,b,d,e,h,j)});u(d,"getCoordinates",function(a,b){var c=this.chart,d={xAxis:[],yAxis:[]};c.polar?t(c.axes,function(a){var e=a.isXAxis,f=a.center,h=b.chartX-f[0]-c.plotLeft,f=b.chartY-f[1]-c.plotTop;d[e?"xAxis":"yAxis"].push({axis:a,value:a.translate(e?Math.PI-Math.atan2(h,f):Math.sqrt(Math.pow(h,2)+Math.pow(f,2)),!0)})}):d=a.call(this,b);return d})})()})(Highcharts); 55 | -------------------------------------------------------------------------------- /sbin/static/js/highcharts/mootools-adapter.js: -------------------------------------------------------------------------------- 1 | /* 2 | Highcharts JS v3.0.10 (2014-03-10) 3 | MooTools adapter 4 | 5 | (c) 2010-2014 Torstein Honsi 6 | 7 | License: www.highcharts.com/license 8 | */ 9 | (function(){var e=window,h=document,f=e.MooTools.version.substring(0,3),i=f==="1.2"||f==="1.1",j=i||f==="1.3",g=e.$extend||function(){return Object.append.apply(Object,arguments)};e.HighchartsAdapter={init:function(a){var b=Fx.prototype,c=b.start,d=Fx.Morph.prototype,e=d.compute;b.start=function(b,d){var e=this.element;if(b.d)this.paths=a.init(e,e.d,this.toD);c.apply(this,arguments);return this};d.compute=function(b,c,d){var f=this.paths;if(f)this.element.attr("d",a.step(f[0],f[1],d,this.toD));else return e.apply(this, 10 | arguments)}},adapterRun:function(a,b){if(b==="width"||b==="height")return parseInt(document.id(a).getStyle(b),10)},getScript:function(a,b){var c=h.getElementsByTagName("head")[0],d=h.createElement("script");d.type="text/javascript";d.src=a;d.onload=b;c.appendChild(d)},animate:function(a,b,c){var d=a.attr,f=c&&c.complete;if(d&&!a.setStyle)a.getStyle=a.attr,a.setStyle=function(){var a=arguments;this.attr.call(this,a[0],a[1][0])},a.$family=function(){return!0},a.getComputedStyle=function(){return a.element.getComputedStyle.apply(a.element, 11 | arguments)};e.HighchartsAdapter.stop(a);c=new Fx.Morph(d?a:document.id(a),g({transition:Fx.Transitions.Quad.easeInOut},c));if(d)c.element=a;if(b.d)c.toD=b.d;f&&c.addEvent("complete",f);c.start(b);a.fx=c},each:function(a,b){return i?$each(a,b):Array.each(a,b)},map:function(a,b){return a.map(b)},grep:function(a,b){return a.filter(b)},inArray:function(a,b,c){return b?b.indexOf(a,c):-1},offset:function(a){a=a.getPosition();return{left:a.x,top:a.y}},extendWithEvents:function(a){a.addEvent||(a.nodeName? 12 | document.id(a):g(a,new Events))},addEvent:function(a,b,c){typeof b==="string"&&(b==="unload"&&(b="beforeunload"),e.HighchartsAdapter.extendWithEvents(a),a.addEvent(b,c))},removeEvent:function(a,b,c){typeof a!=="string"&&a.addEvent&&(b?(b==="unload"&&(b="beforeunload"),c?a.removeEvent(b,c):a.removeEvents&&a.removeEvents(b)):a.removeEvents())},fireEvent:function(a,b,c,d){b={type:b,target:a};b=j?new Event(b):new DOMEvent(b);b=g(b,c);if(!b.target&&b.event)b.target=b.event.target;b.preventDefault=function(){d= 13 | null};a.fireEvent&&a.fireEvent(b.type,b);d&&d(b)},washMouseEvent:function(a){if(a.page)a.pageX=a.page.x,a.pageY=a.page.y;return a},stop:function(a){a.fx&&a.fx.cancel()}}})(); 14 | -------------------------------------------------------------------------------- /sbin/static/js/highcharts/prototype-adapter.js: -------------------------------------------------------------------------------- 1 | /* 2 | Highcharts JS v3.0.10 (2014-03-10) 3 | Prototype adapter 4 | 5 | @author Michael Nelson, Torstein Honsi. 6 | 7 | Feel free to use and modify this script. 8 | Highcharts license: www.highcharts.com/license. 9 | */ 10 | var HighchartsAdapter=function(){var f=typeof Effect!=="undefined";return{init:function(a){if(f)Effect.HighchartsTransition=Class.create(Effect.Base,{initialize:function(b,c,d,g){var e;this.element=b;this.key=c;e=b.attr?b.attr(c):$(b).getStyle(c);if(c==="d")this.paths=a.init(b,b.d,d),this.toD=d,e=0,d=1;this.start(Object.extend(g||{},{from:e,to:d,attribute:c}))},setup:function(){HighchartsAdapter._extend(this.element);if(!this.element._highchart_animation)this.element._highchart_animation={};this.element._highchart_animation[this.key]= 11 | this},update:function(b){var c=this.paths,d=this.element;c&&(b=a.step(c[0],c[1],b,this.toD));d.attr?d.element&&d.attr(this.options.attribute,b):(c={},c[this.options.attribute]=b,$(d).setStyle(c))},finish:function(){this.element&&this.element._highchart_animation&&delete this.element._highchart_animation[this.key]}})},adapterRun:function(a,b){return parseInt($(a).getStyle(b),10)},getScript:function(a,b){var c=$$("head")[0];c&&c.appendChild((new Element("script",{type:"text/javascript",src:a})).observe("load", 12 | b))},addNS:function(a){var b=/^(?:click|mouse(?:down|up|over|move|out))$/;return/^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/.test(a)||b.test(a)?a:"h:"+a},addEvent:function(a,b,c){a.addEventListener||a.attachEvent?Event.observe($(a),HighchartsAdapter.addNS(b),c):(HighchartsAdapter._extend(a),a._highcharts_observe(b,c))},animate:function(a,b,c){var d,c=c||{};c.delay=0;c.duration=(c.duration||500)/1E3;c.afterFinish=c.complete;if(f)for(d in b)new Effect.HighchartsTransition($(a), 13 | d,b[d],c);else{if(a.attr)for(d in b)a.attr(d,b[d]);c.complete&&c.complete()}a.attr||$(a).setStyle(b)},stop:function(a){var b;if(a._highcharts_extended&&a._highchart_animation)for(b in a._highchart_animation)a._highchart_animation[b].cancel()},each:function(a,b){$A(a).each(b)},inArray:function(a,b,c){return b?b.indexOf(a,c):-1},offset:function(a){return $(a).cumulativeOffset()},fireEvent:function(a,b,c,d){a.fire?a.fire(HighchartsAdapter.addNS(b),c):a._highcharts_extended&&(c=c||{},a._highcharts_fire(b, 14 | c));c&&c.defaultPrevented&&(d=null);d&&d(c)},removeEvent:function(a,b,c){$(a).stopObserving&&(b&&(b=HighchartsAdapter.addNS(b)),$(a).stopObserving(b,c));window===a?Event.stopObserving(a,b,c):(HighchartsAdapter._extend(a),a._highcharts_stop_observing(b,c))},washMouseEvent:function(a){return a},grep:function(a,b){return a.findAll(b)},map:function(a,b){return a.map(b)},_extend:function(a){a._highcharts_extended||Object.extend(a,{_highchart_events:{},_highchart_animation:null,_highcharts_extended:!0, 15 | _highcharts_observe:function(b,a){this._highchart_events[b]=[this._highchart_events[b],a].compact().flatten()},_highcharts_stop_observing:function(b,a){b?a?this._highchart_events[b]=[this._highchart_events[b]].compact().flatten().without(a):delete this._highchart_events[b]:this._highchart_events={}},_highcharts_fire:function(a,c){var d=this;(this._highchart_events[a]||[]).each(function(a){if(!c.stopped)c.preventDefault=function(){c.defaultPrevented=!0},c.target=d,a.bind(this)(c)===!1&&c.preventDefault()}.bind(this))}})}}}(); 16 | -------------------------------------------------------------------------------- /sbin/static/js/html5shiv.js: -------------------------------------------------------------------------------- 1 | /* 2 | HTML5 Shiv v3.6.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); 5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; 6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup main mark meter nav output progress section summary time video",version:"3.6.2",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment(); 8 | for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d 2 | 3 | 4 | Error 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | {% if status['except']=='teamview-not-allowed' %} 13 | Page not allowed 14 | You are not allowed to view this page. If you think you should have access to this teams page, contact your administrator. 15 | {% endif %} 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
You are not allowed to view this page. If you think you should have access to this teams page, contact your administrator.