├── excel ├── Advanced Search.xlsm └── README.md ├── incident_overview ├── screenshot.png ├── src │ └── main │ │ ├── webapp │ │ ├── images │ │ │ ├── flourish4.png │ │ │ ├── user-profile_32.png │ │ │ └── user-profile-light_32.png │ │ ├── WEB-INF │ │ │ └── web.xml │ │ ├── map │ │ │ ├── css │ │ │ │ ├── d3.parcoords.css │ │ │ │ ├── main.css │ │ │ │ ├── tipsy.css │ │ │ │ ├── throbber.css │ │ │ │ └── floatingCircles.css │ │ │ └── js │ │ │ │ └── jquery.tipsy.js │ │ ├── css │ │ │ ├── d3.slider.css │ │ │ └── vis.css │ │ ├── js │ │ │ ├── queue.v1.min.js │ │ │ ├── jquery.vticker.min.js │ │ │ ├── topojson.v1.min.js │ │ │ └── d3.slider.js │ │ └── index.jsp │ │ ├── resources │ │ ├── qradar.properties │ │ ├── META-INF │ │ │ └── persistence.xml │ │ └── log4j.xml │ │ ├── java │ │ └── com │ │ │ └── ibm │ │ │ └── si │ │ │ └── qradar │ │ │ └── offenseviz │ │ │ ├── core │ │ │ ├── Status.java │ │ │ ├── OffenseCleaner.java │ │ │ ├── OffenseVizContextListener.java │ │ │ └── OffenseCollector.java │ │ │ ├── dao │ │ │ ├── Dao.java │ │ │ ├── BaseDao.java │ │ │ ├── SourceDao.java │ │ │ ├── DestinationDao.java │ │ │ └── OffenseDao.java │ │ │ ├── jpa │ │ │ ├── GeographicEndpoint.java │ │ │ ├── Source.java │ │ │ └── Destination.java │ │ │ ├── util │ │ │ ├── XForceToken.java │ │ │ ├── JsonProcessingExceptionMapper.java │ │ │ ├── PersistenceUtil.java │ │ │ ├── PropertiesReader.java │ │ │ └── XForceUtil.java │ │ │ ├── api │ │ │ ├── OffenseVizApplication.java │ │ │ ├── IPLookupResource.java │ │ │ └── OffenseResource.java │ │ │ ├── geoip │ │ │ ├── GeoInfo.java │ │ │ └── GeoipUtil.java │ │ │ ├── model │ │ │ └── OffenseBucket.java │ │ │ └── conf │ │ │ └── QRadarConfig.java │ │ ├── etc │ │ └── server.xml │ │ └── sql │ │ └── create-db.sql ├── build.gradle ├── README.md └── LICENSE ├── offense_visualizer ├── screenshot.png ├── views.py ├── manifest.json ├── package_as_app.sh ├── README.md ├── visualizer.html └── visualizer.js ├── README.md └── LICENSE /excel/Advanced Search.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibm-security-intelligence/visualizations/HEAD/excel/Advanced Search.xlsm -------------------------------------------------------------------------------- /incident_overview/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibm-security-intelligence/visualizations/HEAD/incident_overview/screenshot.png -------------------------------------------------------------------------------- /offense_visualizer/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibm-security-intelligence/visualizations/HEAD/offense_visualizer/screenshot.png -------------------------------------------------------------------------------- /offense_visualizer/views.py: -------------------------------------------------------------------------------- 1 | __author__ = 'IBM' 2 | 3 | from app import app 4 | 5 | @app.route('/') 6 | def index(): 7 | return "Hello, World!" 8 | -------------------------------------------------------------------------------- /incident_overview/src/main/webapp/images/flourish4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibm-security-intelligence/visualizations/HEAD/incident_overview/src/main/webapp/images/flourish4.png -------------------------------------------------------------------------------- /incident_overview/src/main/webapp/images/user-profile_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibm-security-intelligence/visualizations/HEAD/incident_overview/src/main/webapp/images/user-profile_32.png -------------------------------------------------------------------------------- /incident_overview/src/main/webapp/images/user-profile-light_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibm-security-intelligence/visualizations/HEAD/incident_overview/src/main/webapp/images/user-profile-light_32.png -------------------------------------------------------------------------------- /offense_visualizer/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Offense Visualizer", 3 | "description":"Offense Visualizer", 4 | "version":"0.0.1", 5 | "uuid":"98b0706a-d07a-498f-b732-966adc53adff", 6 | "log_level":"INFO", 7 | 8 | "areas": [{ 9 | "url": "static/visualizer.html", 10 | "text": "Offense Visualizer", 11 | "id": "OffenseVisualizer", 12 | "description": "Offense Visualizer" 13 | }] 14 | } 15 | 16 | -------------------------------------------------------------------------------- /incident_overview/src/main/resources/qradar.properties: -------------------------------------------------------------------------------- 1 | auth_token=dbcb19b4-b7bf-41de-ab6c-18b03c16ab68 2 | url=9.21.118.219 3 | qradar_timeZone=America/Halifax 4 | update_interval_seconds=300 5 | default_latitude=45.945332 6 | default_longitude=-66.691904 7 | cleanup_old_offense_interval=60 8 | xforce_api_url=xforce-api.mybluemix.net 9 | xforce_web_url=exchange.xforce.ibmcloud.com 10 | default_country=Canada 11 | default_city=Fredericton -------------------------------------------------------------------------------- /incident_overview/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jdbc/postgres 5 | com.ibm.si.qradar.offenseviz.jpa.Offense 6 | com.ibm.si.qradar.offenseviz.jpa.Destination 7 | com.ibm.si.qradar.offenseviz.jpa.Source 8 | 9 | 10 | -------------------------------------------------------------------------------- /offense_visualizer/package_as_app.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | FILE=com.ibm.offense_visualizer.0.0.1.zip 3 | 4 | echo "Enter the IP or hostname of your QRadar console:" 5 | read HOST 6 | echo 7 | 8 | DIR="`dirname $0`" 9 | cd $DIR 10 | mkdir -p app/static 11 | cp *.py app 12 | cp *.js app/static 13 | cp *.css app/static 14 | cp *.html app/static 15 | rm -f $FILE 16 | zip -r $FILE app manifest.json 17 | rm -Rf app 18 | echo 19 | 20 | curl -u admin -k --header "Content-Type: application/zip" --data-binary "@$FILE" https://$HOST/api/gui_app_framework/application_creation_task 21 | 22 | echo 23 | echo "If the above text contains CREATING, then this operation succeeded. Wait a few minutes, then refresh the QRadar UI" 24 | -------------------------------------------------------------------------------- /excel/README.md: -------------------------------------------------------------------------------- 1 | Advanced Search.xlsx 2 | 3 | This spreadsheet allows to run a QRadar advanced search and return the resuls directly into Excel for 4 | visualisation and/or further data analysis. 5 | 6 | The spreadsheet is very straightforward. On the first work sheet, called "Config", you configure: 7 | 8 | - the QRadar service IP address, 9 | - the security token to use to access the server (created from the "Authorized services" icon in the admin tab) 10 | - The name of worksheet into which the query results will be placed 11 | - The query itself 12 | 13 | Once you have done this, simply hit the 'Run Query' button, which will invoke a macro and run the query. 14 | 15 | It is recommended not to pull millions of events or flows back into excel, instead your query should filter and/or aggregate the data -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/core/Status.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.core; 14 | 15 | public enum Status { 16 | OPEN, 17 | HIDDEN, 18 | CLOSED 19 | } 20 | -------------------------------------------------------------------------------- /incident_overview/src/main/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | This repository contains samples of visualization add-ons to QRadar, utilizing public REST APIs. 3 | 4 | These samples are provided for reference purposes on an "as is" basis, and are without warranties of any kind. 5 | 6 | Any issues discovered using the samples should not be directed to QRadar support, but be reported on the Github issues tracker. 7 | 8 | incident_overview 9 | ================== 10 | A stand alone visualization that displays incidents from QRadar. Incidents are represented based on magnitude and linked via IP addresses. Details (including geographic map and IP relationship chart) of the offense are available by clicking on an incident. Originally shown at RSA 2015 and Blackhat 2015. 11 | 12 | offense_visualizer 13 | ============== 14 | A visualization that runs in a web browser, showing an interactive bubble chart of offenses 15 | 16 | excel 17 | ===== 18 | Ability to execute an advanced query directly from excel and have the results come back into excel 19 | -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/dao/Dao.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.dao; 14 | 15 | import java.util.List; 16 | 17 | public interface Dao { 18 | void persist(E entity); 19 | void remove(E entity); 20 | E findById(K id); 21 | List findAll(); 22 | } 23 | -------------------------------------------------------------------------------- /incident_overview/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | offense-viz 4 | 5 | index.html 6 | index.htm 7 | index.jsp 8 | default.html 9 | default.htm 10 | default.jsp 11 | 12 | 13 | 14 | com.ibm.si.qradar.offenseviz.api.OffenseVizApplication 15 | 16 | 17 | com.ibm.si.qradar.offenseviz.api.OffenseVizApplication 18 | /api/* 19 | 20 | 21 | 22 | com.ibm.si.qradar.offenseviz.core.OffenseVizContextListener 23 | 24 | 25 | -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/jpa/GeographicEndpoint.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.jpa; 14 | 15 | public interface GeographicEndpoint { 16 | 17 | public String getIp(); 18 | 19 | public void setLatitude(Double latitude); 20 | 21 | public void setLongitude(Double longitude); 22 | 23 | public void setCountry(String country); 24 | 25 | public void setCity(String city); 26 | 27 | public void setInternal(boolean isInternal); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/util/XForceToken.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.util; 14 | 15 | import com.fasterxml.jackson.annotation.JsonProperty; 16 | 17 | public class XForceToken { 18 | 19 | @JsonProperty("token") 20 | private String token; 21 | 22 | public XForceToken() {} 23 | 24 | public String getToken() { 25 | return token; 26 | } 27 | 28 | public void setToken(String token) { 29 | this.token = token; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /incident_overview/src/main/webapp/map/css/d3.parcoords.css: -------------------------------------------------------------------------------- 1 | .parcoords > svg, .parcoords > canvas { 2 | font-size: 16px; 3 | font-family: Helvetica, Arial, sans-serif; 4 | position: absolute; 5 | } 6 | 7 | .parcoords .axis .tick > text { 8 | font-size: 10px; 9 | } 10 | 11 | .parcoords > canvas { 12 | pointer-events: none; 13 | } 14 | 15 | .parcoords text.label { 16 | cursor: default; 17 | } 18 | 19 | .parcoords rect.background { 20 | fill: transparent; 21 | } 22 | .parcoords rect.background:hover { 23 | fill: rgba(120,120,120,0.2); 24 | } 25 | .parcoords .resize rect { 26 | fill: rgba(0,0,0,0.1); 27 | } 28 | .parcoords rect.extent { 29 | fill: rgba(255,255,255,0.25); 30 | stroke: rgba(0,0,0,0.6); 31 | } 32 | .parcoords .axis line, .parcoords .axis path { 33 | fill: none; 34 | stroke: #222; 35 | shape-rendering: crispEdges; 36 | } 37 | .parcoords canvas { 38 | opacity: 1; 39 | -moz-transition: opacity 0.3s; 40 | -webkit-transition: opacity 0.3s; 41 | -o-transition: opacity 0.3s; 42 | } 43 | .parcoords canvas.faded { 44 | opacity: 0.25; 45 | } 46 | .parcoords { 47 | -webkit-touch-callout: none; 48 | -webkit-user-select: none; 49 | -khtml-user-select: none; 50 | -moz-user-select: none; 51 | -ms-user-select: none; 52 | user-select: none; 53 | } 54 | -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/dao/BaseDao.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.dao; 14 | 15 | import javax.persistence.EntityManager; 16 | import javax.persistence.PersistenceContext; 17 | 18 | import com.ibm.si.qradar.offenseviz.util.PersistenceUtil; 19 | 20 | public abstract class BaseDao implements Dao{ 21 | 22 | protected EntityManager entityManager; 23 | 24 | public BaseDao(EntityManager entityManager) { 25 | this.entityManager = entityManager; 26 | } 27 | 28 | public EntityManager getEntityManager() { 29 | return entityManager; 30 | } 31 | 32 | public void setEntityManager(EntityManager entityManager) { 33 | this.entityManager = entityManager; 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/util/JsonProcessingExceptionMapper.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.util; 14 | 15 | import com.fasterxml.jackson.core.JsonProcessingException; 16 | 17 | import javax.ws.rs.core.MediaType; 18 | import javax.ws.rs.core.Response; 19 | import javax.ws.rs.core.Response.Status; 20 | import javax.ws.rs.ext.ExceptionMapper; 21 | import javax.ws.rs.ext.Provider; 22 | 23 | @Provider 24 | public class JsonProcessingExceptionMapper implements ExceptionMapper { 25 | 26 | @Override 27 | public Response toResponse(JsonProcessingException exception) { 28 | String message = exception.getMessage(); 29 | return Response.status(Status.BAD_REQUEST).type(MediaType.TEXT_PLAIN) 30 | .entity("The supplied JSON was not well formed: " + message) 31 | .build(); 32 | } 33 | } -------------------------------------------------------------------------------- /incident_overview/src/main/etc/server.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jsp-2.2 6 | localConnector-1.0 7 | jaxrs-1.1 8 | jdbc-4.0 9 | jpa-2.0 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/util/PersistenceUtil.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.util; 14 | 15 | import javax.persistence.EntityManagerFactory; 16 | import javax.persistence.Persistence; 17 | import org.slf4j.LoggerFactory; 18 | import org.slf4j.Logger; 19 | 20 | public class PersistenceUtil { 21 | 22 | private static final EntityManagerFactory entityManagerFactory; 23 | private static final Logger logger = LoggerFactory.getLogger(PersistenceUtil.class); 24 | 25 | static { 26 | try { 27 | entityManagerFactory = Persistence.createEntityManagerFactory("offense-viz"); 28 | } catch (Throwable e) { 29 | logger.error("Initial SessionFactory creation failed " + e.getMessage()); 30 | throw new ExceptionInInitializerError(e); 31 | } 32 | } 33 | 34 | public static EntityManagerFactory getEntityManagerFactory() { 35 | return entityManagerFactory; 36 | } 37 | 38 | } 39 | 40 | -------------------------------------------------------------------------------- /incident_overview/src/main/webapp/css/d3.slider.css: -------------------------------------------------------------------------------- 1 | .d3-slider { 2 | position: relative; 3 | font-family: Verdana,Arial,sans-serif; 4 | font-size: 1.1em; 5 | border: 1px solid #aaaaaa; 6 | z-index: 2; 7 | } 8 | 9 | .d3-slider-horizontal { 10 | height: .8em; 11 | } 12 | 13 | .d3-slider-range { 14 | background:#2980b9; 15 | left:0px; 16 | right:0px; 17 | height: 0.8em; 18 | position: absolute; 19 | } 20 | 21 | .d3-slider-range-vertical { 22 | background:#2980b9; 23 | left:0px; 24 | right:0px; 25 | position: absolute; 26 | top:0; 27 | } 28 | 29 | .d3-slider-vertical { 30 | width: .8em; 31 | height: 100px; 32 | } 33 | 34 | .d3-slider-handle { 35 | position: absolute; 36 | width: 1.2em; 37 | height: 1.2em; 38 | border: 1px solid #d3d3d3; 39 | border-radius: 4px; 40 | background: #eee; 41 | background: linear-gradient(to bottom, #eee 0%, #ddd 100%); 42 | z-index: 3; 43 | } 44 | 45 | .d3-slider-handle:hover { 46 | border: 1px solid #999999; 47 | } 48 | 49 | .d3-slider-horizontal .d3-slider-handle { 50 | top: -.3em; 51 | margin-left: -.6em; 52 | } 53 | 54 | .d3-slider-axis { 55 | position: relative; 56 | z-index: 1; 57 | } 58 | 59 | .d3-slider-axis-bottom { 60 | top: .8em; 61 | } 62 | 63 | .d3-slider-axis-right { 64 | left: .8em; 65 | } 66 | 67 | .d3-slider-axis path { 68 | stroke-width: 0; 69 | fill: none; 70 | } 71 | 72 | .d3-slider-axis line { 73 | fill: none; 74 | stroke: #aaa; 75 | shape-rendering: crispEdges; 76 | } 77 | 78 | .d3-slider-axis text { 79 | font-size: 11px; 80 | } 81 | 82 | .d3-slider-vertical .d3-slider-handle { 83 | left: -.25em; 84 | margin-left: 0; 85 | margin-bottom: -.6em; 86 | } -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/util/PropertiesReader.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.util; 14 | 15 | import java.io.FileInputStream; 16 | import java.io.FileNotFoundException; 17 | import java.io.IOException; 18 | import java.io.InputStream; 19 | import java.util.Properties; 20 | 21 | public class PropertiesReader { 22 | 23 | public Properties getProperties(String filename) throws IOException { 24 | Properties properties = new Properties(); 25 | InputStream inputStream = getClass().getClassLoader().getResourceAsStream(filename); 26 | 27 | if (inputStream != null) { 28 | properties.load(inputStream); 29 | } else { 30 | throw new FileNotFoundException(String.format("Properties file %s was not found in the classpath", filename)); 31 | } 32 | 33 | return properties; 34 | } 35 | 36 | public Properties getPropertiesFile(String filename) throws IOException { 37 | Properties properties = new Properties(); 38 | InputStream inputStream = new FileInputStream("/opt/" + filename); 39 | properties.load(inputStream); 40 | return properties; 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /incident_overview/src/main/sql/create-db.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE offenseviz; 2 | 3 | CREATE TABLE offense( 4 | id BIGSERIAL PRIMARY KEY NOT NULL, 5 | seen_at TIMESTAMP WITHOUT TIME ZONE DEFAULT now(), 6 | offense_id BIGINT, 7 | credibility INT, 8 | remote_destination_count INT, 9 | assigned_to TEXT, 10 | source_count INT, 11 | start_time TIMESTAMP WITHOUT TIME ZONE, 12 | inactive BOOLEAN, 13 | protected BOOLEAN, 14 | policy_category_count INT, 15 | description TEXT, 16 | category_count INT, 17 | relevance INT, 18 | device_count INT, 19 | security_category_count INT, 20 | flow_count INT, 21 | event_count INT, 22 | offense_source TEXT, 23 | status VARCHAR(16), 24 | magnitude INT, 25 | severity INT, 26 | username_count INT, 27 | closing_user TEXT, 28 | follow_up BOOLEAN, 29 | closing_reason_id INT, 30 | close_time TIMESTAMP WITHOUT TIME ZONE, 31 | source_network TEXT, 32 | last_updated_time TIMESTAMP WITHOUT TIME ZONE, 33 | offense_type INT 34 | ); 35 | 36 | CREATE TABLE source ( 37 | id BIGINT PRIMARY KEY NOT NULL, 38 | ip VARCHAR(16), 39 | network TEXT, 40 | username TEXT, 41 | latitude double precision, 42 | longitude double precision, 43 | country VARCHAR(100), 44 | city VARCHAR(100), 45 | internal BOOLEAN NOT NULL default false 46 | ); 47 | 48 | CREATE TABLE destination ( 49 | id BIGINT PRIMARY KEY NOT NULL, 50 | ip VARCHAR(16), 51 | network TEXT, 52 | username TEXT, 53 | latitude double precision, 54 | longitude double precision, 55 | country VARCHAR(100), 56 | city VARCHAR(100), 57 | internal BOOLEAN NOT NULL default false 58 | ); 59 | 60 | CREATE TABLE offense_source_link ( 61 | id BIGSERIAL PRIMARY KEY NOT NULL, 62 | offense_id BIGINT REFERENCES offense(id), 63 | source_id BIGINT REFERENCES source(id) 64 | ); 65 | 66 | CREATE TABLE offense_dest_link ( 67 | id BIGSERIAL PRIMARY KEY NOT NULL, 68 | offense_id BIGINT REFERENCES offense(id), 69 | dest_id BIGINT REFERENCES destination(id) 70 | ); 71 | 72 | CREATE TABLE offense_category ( 73 | offense_id BIGINT REFERENCES offense(id) NOT NULL, 74 | name TEXT NOT NULL 75 | ); 76 | -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/api/OffenseVizApplication.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.api; 14 | 15 | import java.util.Collections; 16 | import java.util.HashSet; 17 | import java.util.Set; 18 | 19 | import javax.ws.rs.core.Application; 20 | 21 | import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; 22 | import com.ibm.si.qradar.offenseviz.util.JsonProcessingExceptionMapper; 23 | 24 | public class OffenseVizApplication extends Application{ 25 | 26 | private Set singletons = Collections.emptySet(); 27 | 28 | public OffenseVizApplication() { 29 | Set s = new HashSet(); 30 | JacksonJsonProvider jsonProvider = new JacksonJsonProvider(); 31 | s.add(jsonProvider); 32 | setSingletons(s); 33 | } 34 | 35 | private void setSingletons(final Set singletons) { 36 | this.singletons = singletons; 37 | } 38 | 39 | public Set> getClasses() { 40 | HashSet> classes = new HashSet>(); 41 | 42 | // Resources 43 | classes.add(OffenseResource.class); 44 | classes.add(IPLookupResource.class); 45 | 46 | // Providers 47 | classes.add(JsonProcessingExceptionMapper.class); 48 | 49 | return classes; 50 | } 51 | 52 | @Override 53 | public Set getSingletons() { 54 | return singletons; 55 | } 56 | } 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/dao/SourceDao.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.dao; 14 | 15 | import java.util.List; 16 | 17 | import javax.persistence.EntityManager; 18 | import javax.persistence.TypedQuery; 19 | 20 | import com.ibm.si.qradar.offenseviz.jpa.Source; 21 | 22 | public class SourceDao extends BaseDao { 23 | 24 | public SourceDao(EntityManager entityManager) { 25 | super(entityManager); 26 | } 27 | 28 | @Override 29 | public void persist(Source entity) { 30 | // TODO Auto-generated method stub 31 | 32 | } 33 | 34 | @Override 35 | public void remove(Source entity) { 36 | // TODO Auto-generated method stub 37 | 38 | } 39 | 40 | @Override 41 | public Source findById(Long id) { 42 | // TODO Auto-generated method stub 43 | return null; 44 | } 45 | 46 | @Override 47 | public List findAll() { 48 | // TODO Auto-generated method stub 49 | return null; 50 | } 51 | 52 | public List findAllIds() { 53 | TypedQuery query = entityManager.createNamedQuery("Source.findAllIds", Long.class); 54 | List results = query.getResultList(); 55 | return results; 56 | } 57 | 58 | public Source merge(Source source) { 59 | entityManager.getTransaction().begin(); 60 | Source merged = entityManager.merge(source); 61 | entityManager.getTransaction().commit(); 62 | return merged; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /offense_visualizer/README.md: -------------------------------------------------------------------------------- 1 | ![Alt text](screenshot.png?raw=true "Screenshot") 2 | 3 | ABOUT 4 | ===== 5 | 6 | This is a sample of how you can build advanced visualizations using the offense API. In this sample, we will use D3 (http://d3js.org) and a helper library called NVD3 (http://nvd3.org) to draw some fancy bubble charts of our offenses. 7 | 8 | 9 | REQUIREMENTS 10 | =========== 11 | 12 | The only requirement to run this sample is an active QRadar instance with offenses to deploy it on. 13 | 14 | DETAILED DESCRIPTION 15 | =========== 16 | 17 | The way this sample works is quite simple. We make a request to the "/api/siem/offenses" endpoint to get all the information we know about offenses encoded as JSON. We then load this information into a graphing framework to draw the offense information based on various attributes. 18 | 19 | HOW TO DEPLOY 20 | =========== 21 | 22 | Method 1, Deploy As App ( 7.2.6 and later only ): 23 | 24 | Copy the files to a machine, and run the package_as_app.sh script. The script will prompt you for the QRadar console IP address, as well as the password for the 'admin' user. It will then deploy the visualization as new tab in QRadar. 25 | 26 | Method 2, Manual ( pre 7.2.6 ): 27 | 28 | To deploy the sample, copy all of the files in this directory (using SCP) to your QRadar server into the location /opt/qradar/webapps/console/offense_visualizer. You can then access the sample at the following URL: https://[QRadar IP]/console/offense_visualizer/visualizer.html - feel free to bookmark this URL for quick reference. 29 | 30 | LICENSE 31 | =========== 32 | 33 | Copyright (c) 2013 IBM 34 | 35 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 36 | compliance with the License. You may obtain a copy of the License at 37 | 38 | http://www.apache.org/licenses/LICENSE-2.0 39 | 40 | Unless required by applicable law or agreed to in writing, software distributed under the License is 41 | distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 42 | See the License for the specific language governing permissions and limitations under the License. 43 | -------------------------------------------------------------------------------- /incident_overview/src/main/webapp/map/css/main.css: -------------------------------------------------------------------------------- 1 | #map { 2 | position:relative; 3 | background-color: #fff; 4 | border: none; 5 | } 6 | .background { 7 | fill: none; 8 | pointer-events: all; 9 | } 10 | .countries { 11 | /*fill: #cde; 12 | stroke: #fff;*/ 13 | fill: #cccccc; 14 | stroke: #fff; 15 | stroke-linejoin: round; 16 | stroke-linecap: round; 17 | } 18 | .markerpoint { 19 | fill: #000000; 20 | stroke: #ffffff; 21 | stroke-linecap: round; 22 | stroke-linejoin: round; 23 | } 24 | 25 | .markerpoint circle { 26 | cursor: pointer; 27 | } 28 | 29 | .markerpoint circle:hover { 30 | /*stroke: #f0ff00;*/ 31 | /*fill: #333333;*/ 32 | opacity: 0.75; 33 | } 34 | 35 | .travelMarker { 36 | stroke: #00649D; 37 | fill: #82D1F5; 38 | opacity: 0.75; 39 | } 40 | 41 | .travelMarker:hover { 42 | /*stroke: #333333;*/ 43 | opacity: 1; 44 | } 45 | 46 | .lineConnect { 47 | stroke: #00BFF2; 48 | fill: none; 49 | opacity: 0.5; 50 | } 51 | 52 | /* .lineConnect:hover { 53 | opacity: 0.75; 54 | } */ 55 | 56 | #graph > svg { 57 | height:150px; 58 | width: inherit; 59 | } 60 | 61 | div.tooltip { 62 | color: #222; 63 | background: #fff; 64 | padding: .5em; 65 | text-shadow: #f5f5f5 0 1px 0; 66 | border-radius: 2px; 67 | box-shadow: 0px 0px 2px 0px #a6a6a6; 68 | opacity: 0.9; 69 | position: fixed; 70 | } 71 | 72 | .overlay { 73 | position:absolute; 74 | top:0; 75 | left:0; 76 | right:0; 77 | bottom:0; 78 | /* background-color:rgba(0, 0, 0, 0.2); */ 79 | background: url(data:;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAABl0RVh0U29mdHdhcmUAUGFpbnQuTkVUIHYzLjUuNUmK/OAAAAATSURBVBhXY2RgYNgHxGAAYuwDAA78AjwwRoQYAAAAAElFTkSuQmCC) repeat scroll transparent\9; 80 | z-index:9999; 81 | color:black; 82 | text-align: center; 83 | } 84 | 85 | .overlay_txt { 86 | display: inline-block; 87 | vertical-align: middle; 88 | margin-top: 100px; 89 | padding: 10px 15px; 90 | position:relative; 91 | font-weight:bold; 92 | } -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/core/OffenseCleaner.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.core; 14 | 15 | import java.sql.Timestamp; 16 | import java.util.Calendar; 17 | 18 | import javax.persistence.EntityManager; 19 | 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import com.ibm.si.qradar.offenseviz.conf.QRadarConfig; 24 | import com.ibm.si.qradar.offenseviz.dao.OffenseDao; 25 | import com.ibm.si.qradar.offenseviz.util.PersistenceUtil; 26 | 27 | public class OffenseCleaner implements Runnable { 28 | 29 | private OffenseDao offenseDao; 30 | private static final Logger logger = LoggerFactory.getLogger(OffenseCleaner.class); 31 | 32 | @Override 33 | public void run() { 34 | EntityManager entityManager = null; 35 | 36 | try { 37 | entityManager = PersistenceUtil.getEntityManagerFactory().createEntityManager(); 38 | offenseDao = new OffenseDao(entityManager); 39 | cleanupOldOffenses(); 40 | } catch (Exception e) { 41 | logger.error("Could not clean up old offenses", e); 42 | } finally { 43 | if(entityManager != null) { 44 | entityManager.close(); 45 | } 46 | } 47 | } 48 | 49 | private void cleanupOldOffenses() { 50 | int cleanupInterval = QRadarConfig.getInstance().getCleanupIntervalMinutes(); 51 | Long now = Calendar.getInstance().getTimeInMillis(); 52 | Long maxAge = now - (cleanupInterval * 60 * 1000); 53 | offenseDao.cleanupOldOffenses(new Timestamp(maxAge)); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /incident_overview/src/main/webapp/js/queue.v1.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012, Michael Bostock 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * The name Michael Bostock may not be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT, 22 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 25 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | !function(){function n(n){function e(){for(;i=ap;){var u=a++,e=c[u],o=t.call(e,1);o.push(l(u)),++p,e[0].apply(null,o)}}function l(n){return function(u,t){--p,null==s&&(null!=u?(s=u,a=d=0/0,o()):(c[n]=t,--d?i||e():o()))}}function o(){null!=s?m(s):f?m(s,c):m.apply(null,[s].concat(c))}var r,i,f,c=[],a=0,p=0,d=0,s=null,m=u;return n||(n=1/0),r={defer:function(){return s||(c.push(arguments),++d,e()),r},await:function(n){return m=n,f=!1,d||o(),r},awaitAll:function(n){return m=n,f=!0,d||o(),r}}}function u(){}var t=[].slice;n.version="1.0.7","function"==typeof define&&define.amd?define(function(){return n}):"object"==typeof module&&module.exports?module.exports=n:this.queue=n}(); -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/dao/DestinationDao.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.dao; 14 | 15 | import java.util.List; 16 | 17 | import javax.persistence.EntityManager; 18 | import javax.persistence.TypedQuery; 19 | 20 | import com.ibm.si.qradar.offenseviz.jpa.Destination; 21 | import com.ibm.si.qradar.offenseviz.jpa.Source; 22 | 23 | public class DestinationDao extends BaseDao { 24 | 25 | public DestinationDao(EntityManager entityManager) { 26 | super(entityManager); 27 | } 28 | 29 | @Override 30 | public void persist(Destination entity) { 31 | // TODO Auto-generated method stub 32 | 33 | } 34 | 35 | @Override 36 | public void remove(Destination entity) { 37 | // TODO Auto-generated method stub 38 | 39 | } 40 | 41 | @Override 42 | public Destination findById(Long id) { 43 | // TODO Auto-generated method stub 44 | return null; 45 | } 46 | 47 | @Override 48 | public List findAll() { 49 | // TODO Auto-generated method stub 50 | return null; 51 | } 52 | 53 | public List findAllIds() { 54 | TypedQuery query = entityManager.createNamedQuery("Destination.findAllIds", Long.class); 55 | List results = query.getResultList(); 56 | return results; 57 | } 58 | 59 | public Destination merge(Destination dest) { 60 | entityManager.getTransaction().begin(); 61 | Destination merged = entityManager.merge(dest); 62 | entityManager.getTransaction().commit(); 63 | return merged; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/core/OffenseVizContextListener.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.core; 14 | 15 | import java.util.concurrent.Executors; 16 | import java.util.concurrent.ScheduledExecutorService; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | import javax.servlet.ServletContext; 20 | import javax.servlet.ServletContextEvent; 21 | import javax.servlet.ServletContextListener; 22 | 23 | import com.ibm.si.qradar.offenseviz.conf.QRadarConfig; 24 | 25 | public class OffenseVizContextListener implements ServletContextListener { 26 | 27 | private ScheduledExecutorService scheduledExecutor; 28 | 29 | public void contextInitialized(ServletContextEvent sce) { 30 | ServletContext context = sce.getServletContext(); 31 | scheduledExecutor = Executors.newSingleThreadScheduledExecutor(); 32 | context.setAttribute("MY_EXECUTOR", scheduledExecutor); 33 | 34 | initStartupJobs(); 35 | } 36 | 37 | private void initStartupJobs() { 38 | int updateInterval = QRadarConfig.getInstance().getUpdateInterval(); 39 | int cleanupInterval = QRadarConfig.getInstance().getCleanupIntervalMinutes(); 40 | scheduledExecutor.scheduleAtFixedRate(new OffenseCollector(), 0, updateInterval, TimeUnit.SECONDS); 41 | scheduledExecutor.scheduleAtFixedRate(new OffenseCleaner(), cleanupInterval, cleanupInterval, TimeUnit.MINUTES); 42 | } 43 | 44 | public void contextDestroyed(ServletContextEvent sce) { 45 | ServletContext context = sce.getServletContext(); 46 | scheduledExecutor.shutdownNow(); // or process/wait until all pending jobs are done 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/geoip/GeoInfo.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.geoip; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | import com.maxmind.geoip2.record.Location; 19 | 20 | public class GeoInfo { 21 | private Double latitude; 22 | private Double longitude; 23 | private String country; 24 | private String city; 25 | 26 | public GeoInfo(Location location) { 27 | this.latitude = location.getLatitude(); 28 | this.longitude = location.getLongitude(); 29 | } 30 | 31 | public GeoInfo(Double latitude, Double longitude, String country, String city) { 32 | this.latitude = latitude; 33 | this.longitude = longitude; 34 | this.country = country; 35 | this.city = city; 36 | } 37 | 38 | public Double getLatitude() { 39 | return latitude; 40 | } 41 | 42 | public void setLatitude(Double latitude) { 43 | this.latitude = latitude; 44 | } 45 | 46 | public Double getLongitude() { 47 | return longitude; 48 | } 49 | 50 | public void setLongitude(Double longitude) { 51 | this.longitude = longitude; 52 | } 53 | 54 | public List asArray() { 55 | List list = new ArrayList(); 56 | list.add(longitude.toString()); 57 | list.add(latitude.toString()); 58 | list.add(country); 59 | list.add(city); 60 | return list; 61 | } 62 | 63 | public String getCountry() { 64 | return country; 65 | } 66 | 67 | public void setCountry(String country) { 68 | this.country = country; 69 | } 70 | 71 | public String getCity() { 72 | return city; 73 | } 74 | 75 | public void setCity(String city) { 76 | this.city = city; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /incident_overview/src/main/webapp/map/css/tipsy.css: -------------------------------------------------------------------------------- 1 | .tipsy { font-size: 10px; position: absolute; padding: 5px; z-index: 100000; } 2 | .tipsy-inner { background-color: #00649D; color: #FFF; max-width: 200px; padding: 5px 8px 4px 8px; text-align: center; } 3 | 4 | /* Rounded corners */ 5 | .tipsy-inner { border-radius: 3px; -moz-border-radius: 3px; -webkit-border-radius: 3px; } 6 | 7 | /* Uncomment for shadow */ 8 | /*.tipsy-inner { box-shadow: 0 0 5px #00649D000; -webkit-box-shadow: 0 0 5px #00649D000; -moz-box-shadow: 0 0 5px #00649D000; }*/ 9 | 10 | .tipsy-arrow { position: absolute; width: 0; height: 0; line-height: 0; border: 5px dashed #00649D; } 11 | 12 | /* Rules to colour arrows */ 13 | .tipsy-arrow-n { border-bottom-color: #00649D; } 14 | .tipsy-arrow-s { border-top-color: #00649D; } 15 | .tipsy-arrow-e { border-left-color: #00649D; } 16 | .tipsy-arrow-w { border-right-color: #00649D; } 17 | 18 | .tipsy-n .tipsy-arrow { top: 0px; left: 50%; margin-left: -5px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent; } 19 | .tipsy-nw .tipsy-arrow { top: 0; left: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;} 20 | .tipsy-ne .tipsy-arrow { top: 0; right: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;} 21 | .tipsy-s .tipsy-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; } 22 | .tipsy-sw .tipsy-arrow { bottom: 0; left: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; } 23 | .tipsy-se .tipsy-arrow { bottom: 0; right: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; } 24 | .tipsy-e .tipsy-arrow { right: 0; top: 50%; margin-top: -5px; border-left-style: solid; border-right: none; border-top-color: transparent; border-bottom-color: transparent; } 25 | .tipsy-w .tipsy-arrow { left: 0; top: 50%; margin-top: -5px; border-right-style: solid; border-left: none; border-top-color: transparent; border-bottom-color: transparent; } 26 | 27 | .tipsy th { 28 | padding-right: 5px; 29 | font-weight: 700; 30 | text-align: left; 31 | } 32 | .tipsy td { 33 | text-align: left; 34 | } 35 | .tipsy table { 36 | margin-bottom: 5px; 37 | } -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/api/IPLookupResource.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.api; 14 | 15 | import javax.ws.rs.GET; 16 | import javax.ws.rs.Path; 17 | import javax.ws.rs.Produces; 18 | import javax.ws.rs.QueryParam; 19 | import javax.ws.rs.core.MediaType; 20 | import javax.ws.rs.core.Response; 21 | 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import com.ibm.si.qradar.offenseviz.conf.QRadarConfig; 26 | import com.ibm.si.qradar.offenseviz.geoip.GeoInfo; 27 | import com.ibm.si.qradar.offenseviz.geoip.GeoipUtil; 28 | 29 | @Path("/iplookup") 30 | public class IPLookupResource { 31 | 32 | private static final Logger logger = LoggerFactory.getLogger(IPLookupResource.class); 33 | private static final Double defaultLatitude = QRadarConfig.getInstance().getDefaultLatitude(); 34 | private static final Double defaultLongitude = QRadarConfig.getInstance().getDefaultLongitude(); 35 | private static final String defaultCountry = QRadarConfig.getInstance().getDefaultCountry(); 36 | private static final String defaultCity = QRadarConfig.getInstance().getDefaultCity(); 37 | 38 | @GET 39 | @Produces(MediaType.APPLICATION_JSON) 40 | public Response lookupIpAddress(@QueryParam("ip") String ipAddress) { 41 | Response response; 42 | GeoipUtil geoipUtil = null; 43 | 44 | if(ipAddress != null) { 45 | GeoInfo ginfo = null; 46 | 47 | try { 48 | geoipUtil = new GeoipUtil(); 49 | ginfo = geoipUtil.getGeoInfo(ipAddress); 50 | if(ginfo == null) { 51 | ginfo = new GeoInfo(defaultLatitude, defaultLongitude, defaultCountry, defaultCity); 52 | } 53 | } catch (Exception e) { 54 | logger.debug("Couldn't look up IP", e); 55 | } finally { 56 | geoipUtil.dispose(); 57 | } 58 | response = Response.ok().entity(ginfo.asArray()).build(); 59 | } else { 60 | response = Response.ok().build(); 61 | } 62 | return response; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/model/OffenseBucket.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.model; 14 | 15 | import java.sql.Timestamp; 16 | import java.text.SimpleDateFormat; 17 | import java.util.ArrayList; 18 | import java.util.Calendar; 19 | import java.util.Date; 20 | import java.util.List; 21 | 22 | import com.ibm.si.qradar.offenseviz.conf.QRadarConfig; 23 | import com.ibm.si.qradar.offenseviz.jpa.Offense; 24 | 25 | public class OffenseBucket { 26 | 27 | private Timestamp bucketTime; 28 | private List offenses; 29 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss"); 30 | 31 | public OffenseBucket(Timestamp bucketTime, List offenses) { 32 | this.bucketTime = bucketTime; 33 | this.offenses = processOffenseList(offenses); 34 | } 35 | 36 | private List processOffenseList(List allOffenses) { 37 | int interval = QRadarConfig.getInstance().getUpdateInterval(); 38 | Timestamp maxAge = calculateMaxAge(interval, bucketTime); 39 | List offensesWithinInterval = new ArrayList(); 40 | 41 | for (Offense offense : allOffenses) { 42 | if (offense.getSeenAt().before(bucketTime) 43 | && offense.getSeenAt().after(maxAge)) { 44 | offensesWithinInterval.add(offense); 45 | } 46 | } 47 | 48 | return offensesWithinInterval; 49 | } 50 | 51 | private Timestamp calculateMaxAge(int interval, Timestamp bucketTime) { 52 | Calendar cal = Calendar.getInstance(); 53 | cal.setTimeInMillis(bucketTime.getTime()); 54 | cal.add(Calendar.SECOND, -interval); 55 | Timestamp maxAge = new Timestamp(cal.getTime().getTime()); 56 | return maxAge; 57 | } 58 | 59 | public Timestamp getBucketTime() { 60 | return bucketTime; 61 | } 62 | 63 | public void setBucketTime(Timestamp bucketTime) { 64 | this.bucketTime = bucketTime; 65 | } 66 | 67 | public List getOffenses() { 68 | return offenses; 69 | } 70 | 71 | public void setOffenses(List offenses) { 72 | this.offenses = offenses; 73 | } 74 | 75 | @Override 76 | public String toString() { 77 | return "OffenseBucket [\n\tbucketTime=" + dateFormat.format(new Date(bucketTime.getTime())) 78 | + ", \n\toffenseCount =" + String.valueOf(offenses.size()) + "\n]"; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/geoip/GeoipUtil.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.geoip; 14 | 15 | import java.io.File; 16 | import java.io.IOException; 17 | import java.net.InetAddress; 18 | import java.net.URISyntaxException; 19 | import java.net.URL; 20 | 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import com.maxmind.geoip2.DatabaseReader; 25 | import com.maxmind.geoip2.exception.GeoIp2Exception; 26 | import com.maxmind.geoip2.model.CityResponse; 27 | import com.maxmind.geoip2.record.City; 28 | import com.maxmind.geoip2.record.Country; 29 | import com.maxmind.geoip2.record.Location; 30 | 31 | public class GeoipUtil { 32 | 33 | private static final Logger logger = LoggerFactory.getLogger(GeoipUtil.class); 34 | static final ClassLoader loader = GeoipUtil.class.getClassLoader(); 35 | String databaseFilename = "GeoLite2-City.mmdb"; 36 | DatabaseReader reader; 37 | File database; 38 | 39 | public GeoipUtil() throws URISyntaxException, IOException { 40 | try { 41 | URL resourceUrl = loader.getResource(databaseFilename); 42 | //database = new File(resourceUrl.toURI()); 43 | database = new File("/opt/" + databaseFilename); 44 | reader = new DatabaseReader.Builder(database).build(); 45 | } catch (Exception e) { 46 | reader = null; 47 | logger.info("Could not load geo lookup db", e); 48 | } 49 | } 50 | 51 | public GeoInfo getGeoInfo(String ipAddressIn ) throws IOException { 52 | if( reader != null) { 53 | try { 54 | InetAddress ipAddress = InetAddress.getByName(ipAddressIn); 55 | CityResponse response = reader.city(ipAddress); 56 | City city = response.getCity(); 57 | Country country = response.getCountry(); 58 | Location loc = response.getLocation(); 59 | 60 | GeoInfo gInfo = new GeoInfo(loc); 61 | gInfo.setCity(city.getName()); 62 | gInfo.setCountry(country.getName()); 63 | 64 | return gInfo; 65 | } catch(GeoIp2Exception geoE) { 66 | logger.info(String.format("Could not lookup country of %s", ipAddressIn)); 67 | return null; 68 | } 69 | } else { 70 | return null; 71 | } 72 | } 73 | 74 | public void dispose() { 75 | try { 76 | if(reader != null) { 77 | reader.close(); 78 | } 79 | } catch (IOException e) { 80 | logger.error("Could not close GeoIP reader.", e); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /incident_overview/src/main/webapp/js/jquery.vticker.min.js: -------------------------------------------------------------------------------- 1 | /*! vTicker 1.15 2 | http://richhollis.github.com/vticker/ | http://richhollis.github.com/vticker/license/ | based on Jubgits vTicker http://www.jugbit.com/jquery-vticker-vertical-news-ticker/ */ 3 | (function(d){var n={speed:700,pause:4E3,showItems:1,mousePause:!0,height:0,animate:!0,margin:0,padding:0,startPaused:!1},c={moveUp:function(a,b){c.animate(a,b,"up")},moveDown:function(a,b){c.animate(a,b,"down")},animate:function(a,b,e){var c=a.itemHeight,f=a.options,k=a.element,g=k.children("ul"),l="up"===e?"li:first":"li:last";k.trigger("vticker.beforeTick");var m=g.children(l).clone(!0);0a.itemCount||f.next.call(this,{animate:b.animate})},startInterval:function(){var a=d(this).data("state"),b= 5 | this;a.intervalId=setInterval(function(){c.nextUsePause.call(b)},a.options.pause)},stopInterval:function(){var a=d(this).data("state");a&&(a.intervalId&&clearInterval(a.intervalId),a.intervalId=void 0)},restartInterval:function(){c.stopInterval.call(this);c.startInterval.call(this)}},f={init:function(a){f.stop.call(this);var b=jQuery.extend({},n);a=d.extend(b,a);var b=d(this),e={itemCount:b.children("ul").children("li").length,itemHeight:0,itemMargin:0,element:b,animating:!1,options:a,isPaused:a.startPaused? 6 | !0:!1,pausedByCode:!1};d(this).data("state",e);b.css({overflow:"hidden",position:"relative"}).children("ul").css({position:"absolute",margin:0,padding:0}).children("li").css({margin:a.margin,padding:a.padding});isNaN(a.height)||0===a.height?(b.children("ul").children("li").each(function(){var a=d(this);a.height()>e.itemHeight&&(e.itemHeight=a.height())}),b.children("ul").children("li").each(function(){d(this).height(e.itemHeight)}),b.height((e.itemHeight+(a.margin+2*a.padding))*a.showItems+a.margin)): 7 | b.height(a.height);var h=this;a.startPaused||c.startInterval.call(h);a.mousePause&&b.bind("mouseenter",function(){!0!==e.isPaused&&(e.pausedByCode=!0,c.stopInterval.call(h),f.pause.call(h,!0))}).bind("mouseleave",function(){if(!0!==e.isPaused||e.pausedByCode)e.pausedByCode=!1,f.pause.call(h,!1),c.startInterval.call(h)})},pause:function(a){var b=d(this).data("state");if(b){if(2>b.itemCount)return!1;b.isPaused=a;b=b.element;a?(d(this).addClass("paused"),b.trigger("vticker.pause")):(d(this).removeClass("paused"), 8 | b.trigger("vticker.resume"))}},next:function(a){var b=d(this).data("state");if(b){if(b.animating||2>b.itemCount)return!1;c.restartInterval.call(this);c.moveUp(b,a)}},prev:function(a){var b=d(this).data("state");if(b){if(b.animating||2>b.itemCount)return!1;c.restartInterval.call(this);c.moveDown(b,a)}},stop:function(){d(this).data("state")&&c.stopInterval.call(this)},remove:function(){var a=d(this).data("state");a&&(c.stopInterval.call(this),a=a.element,a.unbind(),a.remove())}};d.fn.vTicker=function(a){if(f[a])return f[a].apply(this, 9 | Array.prototype.slice.call(arguments,1));if("object"!==typeof a&&a)d.error("Method "+a+" does not exist on jQuery.vTicker");else return f.init.apply(this,arguments)}})(jQuery); -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/dao/OffenseDao.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.dao; 14 | 15 | import java.sql.Timestamp; 16 | import java.util.List; 17 | 18 | import javax.persistence.EntityManager; 19 | import javax.persistence.TypedQuery; 20 | 21 | import com.ibm.si.qradar.offenseviz.jpa.Destination; 22 | import com.ibm.si.qradar.offenseviz.jpa.Offense; 23 | import com.ibm.si.qradar.offenseviz.jpa.Source; 24 | 25 | public class OffenseDao extends BaseDao{ 26 | 27 | public OffenseDao(EntityManager entityManager) { 28 | super(entityManager); 29 | } 30 | 31 | @Override 32 | public void persist(Offense offense) { 33 | entityManager.getTransaction().begin(); 34 | entityManager.persist(offense); 35 | entityManager.getTransaction().commit(); 36 | } 37 | 38 | @Override 39 | public void remove(Offense offense) { 40 | // TODO Auto-generated method stub 41 | 42 | } 43 | 44 | @Override 45 | public Offense findById(Long id) { 46 | // TODO Auto-generated method stub 47 | return null; 48 | } 49 | 50 | @Override 51 | public List findAll() { 52 | TypedQuery query = entityManager.createNamedQuery("Offense.findAll", Offense.class); 53 | List results = query.getResultList(); 54 | return results; 55 | } 56 | 57 | public List findRecent(Timestamp oldestLastSeenTime) { 58 | TypedQuery query = entityManager.createNamedQuery("Offense.findRecent", Offense.class); 59 | query.setParameter("min_last_seen_time", oldestLastSeenTime); 60 | List results = query.getResultList(); 61 | return results; 62 | } 63 | 64 | public List findByTimestamp(Timestamp oldest, Timestamp newest) { 65 | TypedQuery query = entityManager.createNamedQuery("Offense.findByTimestamp", Offense.class); 66 | query.setParameter("min_last_seen_time", oldest); 67 | query.setParameter("max_last_seen_time", newest); 68 | List results = query.getResultList(); 69 | return results; 70 | } 71 | 72 | public void cleanupOldOffenses(Timestamp maxAge) { 73 | TypedQuery query = entityManager.createNamedQuery("Offense.cleanupOldByTimestamp", Offense.class); 74 | query.setParameter("max_last_seen_time", maxAge); 75 | List offensesToDelete = query.getResultList(); 76 | 77 | entityManager.getTransaction().begin(); 78 | 79 | for(Offense offense : offensesToDelete) { 80 | List sources = offense.getSourceList(); 81 | for(int i = sources.size() - 1; i >= 0 ; i--) { 82 | offense.getSourceList().remove(i); 83 | } 84 | 85 | List dests = offense.getDestinationList(); 86 | for(int i = dests.size() - 1; i >= 0 ; i--) { 87 | offense.getDestinationList().remove(i); 88 | } 89 | 90 | entityManager.remove(offense); 91 | } 92 | 93 | entityManager.getTransaction().commit(); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/jpa/Source.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.jpa; 14 | 15 | import java.io.Serializable; 16 | 17 | import javax.persistence.Entity; 18 | import javax.persistence.Id; 19 | import javax.persistence.NamedQueries; 20 | import javax.persistence.NamedQuery; 21 | 22 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 23 | import com.fasterxml.jackson.annotation.JsonProperty; 24 | 25 | 26 | /** 27 | * The persistent class for the source database table. 28 | * 29 | */ 30 | @Entity 31 | @NamedQueries({ 32 | @NamedQuery(name="Source.findAll", query="SELECT s FROM Source s"), 33 | @NamedQuery(name="Source.findAllIds", query="SELECT s.id FROM Source s"), 34 | }) 35 | @JsonIgnoreProperties(ignoreUnknown=true) 36 | public class Source implements Serializable, GeographicEndpoint { 37 | private static final long serialVersionUID = 1L; 38 | 39 | @Id 40 | private Long id; 41 | 42 | @JsonProperty("source_ip") 43 | private String ip; 44 | 45 | private String network; 46 | 47 | @JsonProperty("user") 48 | private String username; 49 | 50 | private Double latitude; 51 | 52 | private Double longitude; 53 | 54 | private String country; 55 | 56 | private String city; 57 | 58 | private boolean internal; 59 | 60 | public Source() { 61 | } 62 | 63 | public Source(Integer id) { 64 | this.id = Long.valueOf(id); 65 | } 66 | 67 | public String getCountry(){ 68 | return this.country; 69 | } 70 | 71 | public void setCountry(String country){ 72 | this.country = country; 73 | } 74 | 75 | public String getCity(){ 76 | return this.city; 77 | } 78 | 79 | public void setCity(String city){ 80 | this.city = city; 81 | } 82 | 83 | public Long getId() { 84 | return this.id; 85 | } 86 | 87 | public void setId(Long id) { 88 | this.id = id; 89 | } 90 | 91 | public String getIp() { 92 | return this.ip; 93 | } 94 | 95 | public void setIp(String ip) { 96 | this.ip = ip; 97 | } 98 | 99 | public String getNetwork() { 100 | return this.network; 101 | } 102 | 103 | public void setNetwork(String network) { 104 | this.network = network; 105 | } 106 | 107 | public String getUsername() { 108 | return this.username; 109 | } 110 | 111 | public void setUsername(String username) { 112 | this.username = username; 113 | } 114 | 115 | public Double getLatitude() { 116 | return latitude; 117 | } 118 | 119 | public void setLatitude(Double latitude) { 120 | this.latitude = latitude; 121 | } 122 | 123 | public Double getLongitude() { 124 | return longitude; 125 | } 126 | 127 | public void setLongitude(Double longitude) { 128 | this.longitude = longitude; 129 | } 130 | 131 | public boolean isInternal() { 132 | return internal; 133 | } 134 | 135 | public void setInternal(boolean internal) { 136 | this.internal = internal; 137 | } 138 | 139 | } -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/jpa/Destination.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.jpa; 14 | 15 | import java.io.Serializable; 16 | 17 | import javax.persistence.Entity; 18 | import javax.persistence.Id; 19 | import javax.persistence.NamedQueries; 20 | import javax.persistence.NamedQuery; 21 | 22 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 23 | import com.fasterxml.jackson.annotation.JsonProperty; 24 | 25 | 26 | /** 27 | * The persistent class for the destination database table. 28 | * 29 | */ 30 | @Entity 31 | @NamedQueries({ 32 | @NamedQuery(name="Destination.findAll", query="SELECT d FROM Destination d"), 33 | @NamedQuery(name="Destination.findAllIds", query="SELECT d.id FROM Destination d"), 34 | }) 35 | @JsonIgnoreProperties(ignoreUnknown=true) 36 | public class Destination implements Serializable, GeographicEndpoint { 37 | private static final long serialVersionUID = 1L; 38 | 39 | @Id 40 | private Long id; 41 | 42 | @JsonProperty("local_destination_ip") 43 | private String ip; 44 | 45 | private String network; 46 | 47 | @JsonProperty("user") 48 | private String username; 49 | 50 | private Double latitude; 51 | 52 | private Double longitude; 53 | 54 | private String country; 55 | 56 | private String city; 57 | 58 | private boolean internal; 59 | 60 | public Destination() { 61 | } 62 | 63 | public Destination(Integer id) { 64 | this.id = Long.valueOf(id); 65 | } 66 | 67 | public String getCountry(){ 68 | return this.country; 69 | } 70 | 71 | public void setCountry(String country){ 72 | this.country = country; 73 | } 74 | 75 | public String getCity(){ 76 | return this.city; 77 | } 78 | 79 | public void setCity(String city){ 80 | this.city = city; 81 | } 82 | 83 | public Long getId() { 84 | return this.id; 85 | } 86 | 87 | public void setId(Long id) { 88 | this.id = id; 89 | } 90 | 91 | public String getIp() { 92 | return this.ip; 93 | } 94 | 95 | public void setIp(String ip) { 96 | this.ip = ip; 97 | } 98 | 99 | public String getNetwork() { 100 | return this.network; 101 | } 102 | 103 | public void setNetwork(String network) { 104 | this.network = network; 105 | } 106 | 107 | public String getUsername() { 108 | return this.username; 109 | } 110 | 111 | public void setUsername(String username) { 112 | this.username = username; 113 | } 114 | 115 | public Double getLatitude() { 116 | return latitude; 117 | } 118 | 119 | public void setLatitude(Double latitude) { 120 | this.latitude = latitude; 121 | } 122 | 123 | public Double getLongitude() { 124 | return longitude; 125 | } 126 | 127 | public void setLongitude(Double longitude) { 128 | this.longitude = longitude; 129 | } 130 | 131 | public boolean isInternal() { 132 | return internal; 133 | } 134 | 135 | public void setInternal(boolean internal) { 136 | this.internal = internal; 137 | } 138 | 139 | } -------------------------------------------------------------------------------- /incident_overview/build.gradle: -------------------------------------------------------------------------------- 1 | import org.gradle.plugins.ide.eclipse.model.Facet 2 | import groovy.xml.MarkupBuilder 3 | 4 | apply plugin: 'java' 5 | apply plugin: 'war' 6 | apply plugin: 'eclipse-wtp' 7 | 8 | jar.archiveName = "incident-overview.jar" 9 | 10 | eclipse{ 11 | wtp { 12 | facet { 13 | facet name: 'jst.java', type: Facet.FacetType.fixed 14 | facet name: 'jst.web', type: Facet.FacetType.fixed 15 | facet name: 'jst.java', version: '1.7' 16 | facet name: 'jst.web', version: '3.0' 17 | facet name: 'wst.jsdt.web', version: '1.0' 18 | facet name: 'jpt.jpa', version: '2.0' 19 | facet name: 'jst.jaxrs', version: '1.1' 20 | 21 | file { 22 | withXml { 23 | def node = it.asNode() 24 | node.appendNode('runtime', [name: 'WebSphere Application Server Liberty Profile']) 25 | } 26 | } 27 | } 28 | 29 | File facetPrefsFile = file('.settings/org.eclipse.wst.common.project.facet.core.prefs.xml') 30 | facetPrefsFile.parentFile.mkdirs() 31 | def writer = new StringWriter() 32 | def xml = new MarkupBuilder(writer) 33 | xml.root() { 34 | facet(id:'jpt.jpa') { 35 | node(name:'libprov') { 36 | attribute(name:'provider-id', value:'jpa-no-op-library-provider') 37 | } 38 | } 39 | facet(id:'jst.jaxrs') { 40 | node(name:'libprov') { 41 | attribute(name:'provider-id', value:'com.ibm.ast.ws.jaxrs.liberty.library') 42 | } 43 | } 44 | } 45 | facetPrefsFile.write(writer.toString()); 46 | 47 | File jptPrefsFile = file('.settings/org.eclipse.jpt.core.prefs') 48 | jptPrefsFile.write('eclipse.preferences.version=1\n') 49 | jptPrefsFile.append('org.eclipse.jpt.core.platform=eclipselink2_0\n') 50 | jptPrefsFile.append('org.eclipse.jpt.jpa.core.discoverAnnotatedClasses=false\n') 51 | } 52 | } 53 | 54 | repositories { 55 | mavenCentral() 56 | } 57 | 58 | dependencies { 59 | providedCompile( 60 | 'javax.servlet:javax.servlet-api:3.1.0', 61 | 'javax.ws.rs:jsr311-api:1.1.1', 62 | 'javax.annotation:jsr250-api:1.0', 63 | 'org.apache.geronimo.specs:geronimo-jpa_2.0_spec:1.1' 64 | ) 65 | compile( 66 | 'log4j:log4j:1.2.17', 67 | 'org.slf4j:slf4j-api:1.7.9', 68 | 'org.slf4j:slf4j-log4j12:1.7.9', 69 | 'org.apache.httpcomponents:httpclient:4.3.6', 70 | 'com.fasterxml.jackson.core:jackson-core:2.5.0', 71 | 'com.fasterxml.jackson.core:jackson-annotations:2.5.0', 72 | 'com.fasterxml.jackson.core:jackson-databind:2.5.0', 73 | 'com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:2.5.0', 74 | 'org.apache.commons:commons-lang3:3.0', 75 | 'com.maxmind.geoip2:geoip2:2.1.0' 76 | ) 77 | } 78 | 79 | configurations { 80 | openjpac { 81 | visible false 82 | } 83 | } 84 | 85 | dependencies { 86 | openjpac ( 87 | 'org.apache.openjpa:openjpa-all:2.2.1' 88 | ) 89 | } 90 | 91 | compileJava { 92 | inputs.files "src/main/java/META-INF/persistence.xml" 93 | } 94 | compileJava << { 95 | logger.info 'Enhancing JPA entities ...' 96 | def pFile = file("src/main/resources/META-INF/persistence.xml") 97 | if( pFile.exists() ) { 98 | ant { 99 | taskdef(name:'openjpac', classname:'org.apache.openjpa.ant.PCEnhancerTask', classpath:configurations.openjpac.asPath) 100 | openjpac { 101 | config (propertiesFile: pFile) 102 | classpath { 103 | sourceSets.main.java.srcDirs.findAll{ it.exists() }.each { pathelement(location: it) } 104 | pathelement(location: sourceSets.main.output.classesDir) 105 | pathelement(path: configurations.compile.asPath) 106 | } 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /incident_overview/src/main/webapp/map/css/throbber.css: -------------------------------------------------------------------------------- 1 | /* 2 | http://css-spinners.com/#/spinners/ 3 | 4 | Copyright (c) 2013 John W. Long and Julia Elman 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | @-webkit-keyframes throbber { 27 | 0% { 28 | background: #dde2e7; 29 | } 30 | 31 | 10% { 32 | background: #6b9dc8; 33 | } 34 | 35 | 40% { 36 | background: #dde2e7; 37 | } 38 | } 39 | 40 | @-moz-keyframes throbber { 41 | 0% { 42 | background: #dde2e7; 43 | } 44 | 45 | 10% { 46 | background: #6b9dc8; 47 | } 48 | 49 | 40% { 50 | background: #dde2e7; 51 | } 52 | } 53 | 54 | @-o-keyframes throbber { 55 | 0% { 56 | background: #dde2e7; 57 | } 58 | 59 | 10% { 60 | background: #6b9dc8; 61 | } 62 | 63 | 40% { 64 | background: #dde2e7; 65 | } 66 | } 67 | 68 | @keyframes throbber { 69 | 0% { 70 | background: #dde2e7; 71 | } 72 | 73 | 10% { 74 | background: #6b9dc8; 75 | } 76 | 77 | 40% { 78 | background: #dde2e7; 79 | } 80 | } 81 | 82 | /* :not(:required) hides these rules from IE9 and below */ 83 | .throbber:not(:required) { 84 | -webkit-animation: throbber 1500ms 300ms infinite ease-out; 85 | -moz-animation: throbber 1500ms 300ms infinite ease-out; 86 | -ms-animation: throbber 1500ms 300ms infinite ease-out; 87 | -o-animation: throbber 1500ms 300ms infinite ease-out; 88 | animation: throbber 1500ms 300ms infinite ease-out; 89 | background: #dde2e7; 90 | display: inline-block; 91 | position: relative; 92 | text-indent: -9999px; 93 | width: 5px; 94 | height: 10px; 95 | margin: 0 5px; 96 | } 97 | .throbber:not(:required):before, .throbber:not(:required):after { 98 | background: #dde2e7; 99 | content: '\x200B'; 100 | display: inline-block; 101 | width: 5px; 102 | height: 10px; 103 | position: absolute; 104 | bottom: 0; 105 | } 106 | .throbber:not(:required):before { 107 | -webkit-animation: throbber 1500ms 150ms infinite ease-out; 108 | -moz-animation: throbber 1500ms 150ms infinite ease-out; 109 | -ms-animation: throbber 1500ms 150ms infinite ease-out; 110 | -o-animation: throbber 1500ms 150ms infinite ease-out; 111 | animation: throbber 1500ms 150ms infinite ease-out; 112 | left: -10px; 113 | } 114 | .throbber:not(:required):after { 115 | -webkit-animation: throbber 1500ms 450ms infinite ease-out; 116 | -moz-animation: throbber 1500ms 450ms infinite ease-out; 117 | -ms-animation: throbber 1500ms 450ms infinite ease-out; 118 | -o-animation: throbber 1500ms 450ms infinite ease-out; 119 | animation: throbber 1500ms 450ms infinite ease-out; 120 | right: -10px; 121 | } -------------------------------------------------------------------------------- /incident_overview/src/main/webapp/map/css/floatingCircles.css: -------------------------------------------------------------------------------- 1 | #floatingCirclesG{ 2 | margin-left: 259px; 3 | position:relative; 4 | width:200px; 5 | height:200px; 6 | -moz-transform:scale(0.6); 7 | -webkit-transform:scale(0.6); 8 | -ms-transform:scale(0.6); 9 | -o-transform:scale(0.6); 10 | transform:scale(0.6); 11 | } 12 | 13 | .f_circleG{ 14 | position:absolute; 15 | background-color:#FFFFFF; 16 | height:36px; 17 | width:36px; 18 | -moz-border-radius:18px; 19 | -moz-animation-name:f_fadeG; 20 | -moz-animation-duration:1.44s; 21 | -moz-animation-iteration-count:infinite; 22 | -moz-animation-direction:normal; 23 | -webkit-border-radius:18px; 24 | -webkit-animation-name:f_fadeG; 25 | -webkit-animation-duration:1.44s; 26 | -webkit-animation-iteration-count:infinite; 27 | -webkit-animation-direction:normal; 28 | -ms-border-radius:18px; 29 | -ms-animation-name:f_fadeG; 30 | -ms-animation-duration:1.44s; 31 | -ms-animation-iteration-count:infinite; 32 | -ms-animation-direction:normal; 33 | -o-border-radius:18px; 34 | -o-animation-name:f_fadeG; 35 | -o-animation-duration:1.44s; 36 | -o-animation-iteration-count:infinite; 37 | -o-animation-direction:normal; 38 | border-radius:18px; 39 | animation-name:f_fadeG; 40 | animation-duration:1.44s; 41 | animation-iteration-count:infinite; 42 | animation-direction:normal; 43 | } 44 | 45 | #frotateG_01{ 46 | left:0; 47 | top:82px; 48 | -moz-animation-delay:0.54s; 49 | -webkit-animation-delay:0.54s; 50 | -ms-animation-delay:0.54s; 51 | -o-animation-delay:0.54s; 52 | animation-delay:0.54s; 53 | } 54 | 55 | #frotateG_02{ 56 | left:24px; 57 | top:24px; 58 | -moz-animation-delay:0.72s; 59 | -webkit-animation-delay:0.72s; 60 | -ms-animation-delay:0.72s; 61 | -o-animation-delay:0.72s; 62 | animation-delay:0.72s; 63 | } 64 | 65 | #frotateG_03{ 66 | left:82px; 67 | top:0; 68 | -moz-animation-delay:0.9s; 69 | -webkit-animation-delay:0.9s; 70 | -ms-animation-delay:0.9s; 71 | -o-animation-delay:0.9s; 72 | animation-delay:0.9s; 73 | } 74 | 75 | #frotateG_04{ 76 | right:24px; 77 | top:24px; 78 | -moz-animation-delay:1.08s; 79 | -webkit-animation-delay:1.08s; 80 | -ms-animation-delay:1.08s; 81 | -o-animation-delay:1.08s; 82 | animation-delay:1.08s; 83 | } 84 | 85 | #frotateG_05{ 86 | right:0; 87 | top:82px; 88 | -moz-animation-delay:1.26s; 89 | -webkit-animation-delay:1.26s; 90 | -ms-animation-delay:1.26s; 91 | -o-animation-delay:1.26s; 92 | animation-delay:1.26s; 93 | } 94 | 95 | #frotateG_06{ 96 | right:24px; 97 | bottom:24px; 98 | -moz-animation-delay:1.44s; 99 | -webkit-animation-delay:1.44s; 100 | -ms-animation-delay:1.44s; 101 | -o-animation-delay:1.44s; 102 | animation-delay:1.44s; 103 | } 104 | 105 | #frotateG_07{ 106 | left:82px; 107 | bottom:0; 108 | -moz-animation-delay:1.62s; 109 | -webkit-animation-delay:1.62s; 110 | -ms-animation-delay:1.62s; 111 | -o-animation-delay:1.62s; 112 | animation-delay:1.62s; 113 | } 114 | 115 | #frotateG_08{ 116 | left:24px; 117 | bottom:24px; 118 | -moz-animation-delay:1.8s; 119 | -webkit-animation-delay:1.8s; 120 | -ms-animation-delay:1.8s; 121 | -o-animation-delay:1.8s; 122 | animation-delay:1.8s; 123 | } 124 | 125 | @-moz-keyframes f_fadeG{ 126 | 0%{ 127 | background-color:#000000} 128 | 129 | 100%{ 130 | background-color:#FFFFFF} 131 | 132 | } 133 | 134 | @-webkit-keyframes f_fadeG{ 135 | 0%{ 136 | background-color:#000000} 137 | 138 | 100%{ 139 | background-color:#FFFFFF} 140 | 141 | } 142 | 143 | @-ms-keyframes f_fadeG{ 144 | 0%{ 145 | background-color:#000000} 146 | 147 | 100%{ 148 | background-color:#FFFFFF} 149 | 150 | } 151 | 152 | @-o-keyframes f_fadeG{ 153 | 0%{ 154 | background-color:#000000} 155 | 156 | 100%{ 157 | background-color:#FFFFFF} 158 | 159 | } 160 | 161 | @keyframes f_fadeG{ 162 | 0%{ 163 | background-color:#000000} 164 | 165 | 100%{ 166 | background-color:#FFFFFF} 167 | 168 | } -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/util/XForceUtil.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.util; 14 | 15 | import java.io.BufferedReader; 16 | import java.io.IOException; 17 | import java.io.InputStreamReader; 18 | import java.net.URI; 19 | import java.net.URISyntaxException; 20 | import java.security.KeyManagementException; 21 | import java.security.KeyStoreException; 22 | import java.security.NoSuchAlgorithmException; 23 | import java.security.cert.CertificateException; 24 | import java.security.cert.X509Certificate; 25 | 26 | import org.apache.http.client.ClientProtocolException; 27 | import org.apache.http.client.methods.CloseableHttpResponse; 28 | import org.apache.http.client.methods.HttpGet; 29 | import org.apache.http.client.utils.URIBuilder; 30 | import org.apache.http.conn.ssl.AllowAllHostnameVerifier; 31 | import org.apache.http.conn.ssl.SSLContextBuilder; 32 | import org.apache.http.conn.ssl.TrustStrategy; 33 | import org.apache.http.impl.client.CloseableHttpClient; 34 | import org.apache.http.impl.client.HttpClients; 35 | 36 | import com.fasterxml.jackson.core.JsonParseException; 37 | import com.fasterxml.jackson.databind.JsonMappingException; 38 | import com.fasterxml.jackson.databind.ObjectMapper; 39 | import com.ibm.si.qradar.offenseviz.conf.QRadarConfig; 40 | 41 | public class XForceUtil { 42 | 43 | private static final String XFORCE_API_AUTH_TOKEN_PATH = "/auth/anonymousToken"; 44 | 45 | public String getXForceAuthToken(String xforceUrl) { 46 | String token = null; 47 | 48 | try { 49 | CloseableHttpClient httpclient = getHttpClient(); 50 | URI uri = getURI(xforceUrl, XFORCE_API_AUTH_TOKEN_PATH); 51 | String json = executeGet(uri, httpclient); 52 | XForceToken xforceToken = getToken(json); 53 | token = xforceToken.getToken(); 54 | } catch (Exception e) { 55 | e.printStackTrace(); 56 | } 57 | 58 | return token; 59 | } 60 | 61 | private CloseableHttpClient getHttpClient() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException { 62 | CloseableHttpClient httpclient = HttpClients.custom(). 63 | setHostnameVerifier(new AllowAllHostnameVerifier()). 64 | setSslcontext(new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() 65 | { 66 | public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException 67 | { 68 | return true; 69 | } 70 | }).build()).build(); 71 | 72 | return httpclient; 73 | } 74 | 75 | private URI getURI(String url, String path) throws URISyntaxException { 76 | URI uri = new URIBuilder().setScheme("https") 77 | .setHost(url) 78 | .setPath(path) 79 | .build(); 80 | 81 | return uri; 82 | } 83 | 84 | private String executeGet(URI uri, CloseableHttpClient httpclient) throws ClientProtocolException, IOException { 85 | HttpGet httpget = new HttpGet(uri); 86 | CloseableHttpResponse response = httpclient.execute(httpget); 87 | BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8")); 88 | String json = reader.readLine(); 89 | response.close(); 90 | return json; 91 | } 92 | 93 | private XForceToken getToken(String json) throws JsonParseException, JsonMappingException, IOException { 94 | ObjectMapper mapper = new ObjectMapper(); 95 | XForceToken token = mapper.readValue(json, XForceToken.class); 96 | return token; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/api/OffenseResource.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.api; 14 | 15 | import java.sql.Timestamp; 16 | import java.text.SimpleDateFormat; 17 | import java.util.ArrayList; 18 | import java.util.Calendar; 19 | import java.util.Date; 20 | import java.util.List; 21 | import java.util.Random; 22 | 23 | import javax.annotation.Resource; 24 | import javax.persistence.EntityManager; 25 | import javax.servlet.ServletContext; 26 | import javax.ws.rs.GET; 27 | import javax.ws.rs.Path; 28 | import javax.ws.rs.Produces; 29 | import javax.ws.rs.QueryParam; 30 | import javax.ws.rs.core.GenericEntity; 31 | import javax.ws.rs.core.MediaType; 32 | import javax.ws.rs.core.Response; 33 | 34 | import org.slf4j.Logger; 35 | import org.slf4j.LoggerFactory; 36 | 37 | import com.google.api.client.util.Lists; 38 | import com.ibm.si.qradar.offenseviz.conf.QRadarConfig; 39 | import com.ibm.si.qradar.offenseviz.dao.OffenseDao; 40 | import com.ibm.si.qradar.offenseviz.jpa.Offense; 41 | import com.ibm.si.qradar.offenseviz.model.OffenseBucket; 42 | import com.ibm.si.qradar.offenseviz.util.PersistenceUtil; 43 | 44 | @Path("/offenses") 45 | public class OffenseResource { 46 | 47 | @Resource ServletContext servletContext; 48 | private static final Logger logger = LoggerFactory.getLogger(OffenseResource.class); 49 | private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); 50 | 51 | @GET 52 | @Produces( MediaType.APPLICATION_JSON ) 53 | public Response getOffenses(@QueryParam("buckets") Integer bucketCount) { 54 | List buckets = buildOffenseBuckets(bucketCount); 55 | 56 | GenericEntity> entity = new GenericEntity>( 57 | Lists.newArrayList(buckets)) { 58 | }; 59 | 60 | Response response = Response.ok().entity(entity).build(); 61 | 62 | return response; 63 | } 64 | 65 | private List buildOffenseBuckets(int bucketCount) { 66 | EntityManager em = PersistenceUtil.getEntityManagerFactory().createEntityManager(); 67 | OffenseDao offenseDao = new OffenseDao(em); 68 | 69 | Long mostRecentBucketMillis = getLastBucketTime(); 70 | Timestamp mostRecentTimestamp = new Timestamp(mostRecentBucketMillis); 71 | Timestamp oldestLastSeenTimestamp = calculateAge(bucketCount, mostRecentBucketMillis); 72 | 73 | List offensesInTimeRange = offenseDao.findByTimestamp(oldestLastSeenTimestamp, mostRecentTimestamp); 74 | em.close(); 75 | 76 | Date now = new Date(mostRecentTimestamp.getTime()); 77 | Date then = new Date(oldestLastSeenTimestamp.getTime()); 78 | 79 | logger.debug(String.format("We found %s offenses in between %s and %s", 80 | offensesInTimeRange.size(), 81 | dateFormat.format(then), 82 | dateFormat.format(now))); 83 | 84 | List buckets = new ArrayList(); 85 | for(int i = 0; i < bucketCount; i++) { 86 | Timestamp bucketTime = calculateAge(i, mostRecentTimestamp.getTime()); 87 | OffenseBucket bucket = new OffenseBucket(bucketTime, offensesInTimeRange); 88 | buckets.add(bucket); 89 | } 90 | 91 | return buckets; 92 | } 93 | 94 | private Long getLastBucketTime() { 95 | Long startupTime = QRadarConfig.getInstance().getStartupTime(); 96 | Long interval = Long.valueOf(QRadarConfig.getInstance().getUpdateInterval() * 1000); 97 | Long now = Calendar.getInstance().getTimeInMillis(); 98 | Long delta = now - startupTime; 99 | Long modulo = delta % interval; 100 | Long maxBucketTime = now - modulo; 101 | 102 | return maxBucketTime; 103 | } 104 | 105 | private Timestamp calculateAge(int bucketCount, long nowMillis) { 106 | int interval = QRadarConfig.getInstance().getUpdateInterval(); 107 | int seconds = interval * bucketCount; 108 | 109 | Timestamp original = new Timestamp(nowMillis); 110 | Calendar cal = Calendar.getInstance(); 111 | cal.setTimeInMillis(original.getTime()); 112 | cal.add(Calendar.SECOND, -seconds); 113 | Timestamp maxAge = new Timestamp(cal.getTimeInMillis()); 114 | 115 | return maxAge; 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/conf/QRadarConfig.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.conf; 14 | 15 | import java.io.IOException; 16 | import java.util.Calendar; 17 | import java.util.Properties; 18 | 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | 22 | import com.ibm.si.qradar.offenseviz.util.PropertiesReader; 23 | import com.ibm.si.qradar.offenseviz.util.XForceToken; 24 | import com.ibm.si.qradar.offenseviz.util.XForceUtil; 25 | 26 | public class QRadarConfig { 27 | 28 | private static final Logger logger = LoggerFactory.getLogger(QRadarConfig.class); 29 | private static QRadarConfig instance = new QRadarConfig(); 30 | private static final String QRADAR_CONFIG_FILENAME = "qradar.properties"; 31 | private static final int DEFAULT_CLEANUP_INTERVAL = 60; 32 | 33 | private String authorizationToken; 34 | private String url; 35 | private String updateInterval; 36 | private Double defaultLongitude; 37 | private Double defaultLatitude; 38 | private String defaultCountry; 39 | private String defaultCity; 40 | private String xforceApiUrl; 41 | private String xforceWebUrl; 42 | private String xforceAuthToken; 43 | private Long startupTime; 44 | private String qradarTimeZone; 45 | private String cleanupIntervalMinutes; 46 | 47 | public String getQradarTimeZone() { 48 | return qradarTimeZone; 49 | } 50 | 51 | public void setQradarTimeZone(String qradarTimeZone) { 52 | this.qradarTimeZone = qradarTimeZone; 53 | } 54 | 55 | public static QRadarConfig getInstance() { 56 | return instance; 57 | } 58 | 59 | private QRadarConfig() { 60 | refreshConfig(); 61 | } 62 | 63 | public void refreshConfig() { 64 | Properties qradarProperties = new Properties(); 65 | PropertiesReader propReader = new PropertiesReader(); 66 | try { 67 | qradarProperties = propReader.getProperties(QRADAR_CONFIG_FILENAME); 68 | authorizationToken = qradarProperties.getProperty("auth_token"); 69 | url = qradarProperties.getProperty("url"); 70 | updateInterval = qradarProperties.getProperty("update_interval_seconds"); 71 | defaultLatitude = Double.valueOf(qradarProperties.getProperty("default_latitude")); 72 | defaultLongitude = Double.valueOf(qradarProperties.getProperty("default_longitude")); 73 | xforceApiUrl = qradarProperties.getProperty("xforce_api_url"); 74 | xforceWebUrl = qradarProperties.getProperty("xforce_web_url"); 75 | defaultCountry = qradarProperties.getProperty("default_country"); 76 | defaultCity = qradarProperties.getProperty("default_city"); 77 | cleanupIntervalMinutes = qradarProperties.getProperty("cleanup_old_offense_interval"); 78 | 79 | XForceUtil xforceTokenUtil = new XForceUtil(); 80 | xforceAuthToken = xforceTokenUtil.getXForceAuthToken(xforceApiUrl); 81 | 82 | Calendar calendar = Calendar.getInstance(); 83 | startupTime = calendar.getTimeInMillis(); 84 | qradarTimeZone = qradarProperties.getProperty("qradar_timeZone"); 85 | 86 | } catch (IOException e) { 87 | logger.error("There was a problem loading email configuration file.", e); 88 | } 89 | } 90 | 91 | public String getAuthorizationToken() { 92 | return authorizationToken; 93 | } 94 | 95 | public String getUrl() { 96 | return url; 97 | } 98 | 99 | public int getUpdateInterval() { 100 | return Integer.parseInt(updateInterval); 101 | } 102 | 103 | public Double getDefaultLongitude() { 104 | return defaultLongitude; 105 | } 106 | 107 | public void setDefaultLongitude(Double defaultLongitude) { 108 | this.defaultLongitude = defaultLongitude; 109 | } 110 | 111 | public Double getDefaultLatitude() { 112 | return defaultLatitude; 113 | } 114 | 115 | public void setDefaultLatitude(Double defaultLatitude) { 116 | this.defaultLatitude = defaultLatitude; 117 | } 118 | 119 | public String getXforceApiUrl() { 120 | return xforceApiUrl; 121 | } 122 | 123 | public void setXforceApiUrl(String xforceApiUrl) { 124 | this.xforceApiUrl = xforceApiUrl; 125 | } 126 | 127 | public String getXforceWebUrl() { 128 | return xforceWebUrl; 129 | } 130 | 131 | public void setXforceWebUrl(String xforceWebUrl) { 132 | this.xforceWebUrl = xforceWebUrl; 133 | } 134 | 135 | public String getXforceAuthToken() { 136 | return xforceAuthToken; 137 | } 138 | 139 | public void setXforceAuthToken(String xforceAuthToken) { 140 | this.xforceAuthToken = xforceAuthToken; 141 | } 142 | 143 | public String getDefaultCountry() { 144 | return defaultCountry; 145 | } 146 | 147 | public void setDefaultCountry(String defaultCountry) { 148 | this.defaultCountry = defaultCountry; 149 | } 150 | 151 | public String getDefaultCity() { 152 | return defaultCity; 153 | } 154 | 155 | public void setDefaultCity(String defaultCity) { 156 | this.defaultCity = defaultCity; 157 | } 158 | 159 | public Long getStartupTime() { 160 | return startupTime; 161 | } 162 | 163 | public int getCleanupIntervalMinutes() { 164 | int minutes; 165 | try { 166 | minutes = Integer.parseInt(this.cleanupIntervalMinutes); 167 | } catch (NumberFormatException e) { 168 | minutes = DEFAULT_CLEANUP_INTERVAL; 169 | } 170 | return minutes; 171 | } 172 | 173 | } 174 | -------------------------------------------------------------------------------- /offense_visualizer/visualizer.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 149 | 193 | 194 | 195 |
196 |
Offense Visualizer - Last 24 Hours
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 | 205 |
206 |
207 |

Graph Options

208 |

X Axis

209 | 223 |

Y Axis

224 | 238 |

Size

239 | 245 |

Colorization

246 | 250 | 251 |
252 | 253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 | 262 | 263 | -------------------------------------------------------------------------------- /incident_overview/src/main/webapp/js/topojson.v1.min.js: -------------------------------------------------------------------------------- 1 | /*Copyright (c) 2012, Michael Bostock 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * The name Michael Bostock may not be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ 27 | 28 | !function(){function t(n,t){function r(t){var r,e=n.arcs[0>t?~t:t],o=e[0];return n.transform?(r=[0,0],e.forEach(function(n){r[0]+=n[0],r[1]+=n[1]})):r=e[e.length-1],0>t?[r,o]:[o,r]}function e(n,t){for(var r in n){var e=n[r];delete t[e.start],delete e.start,delete e.end,e.forEach(function(n){o[0>n?~n:n]=1}),f.push(e)}}var o={},i={},u={},f=[],c=-1;return t.forEach(function(r,e){var o,i=n.arcs[0>r?~r:r];i.length<3&&!i[1][0]&&!i[1][1]&&(o=t[++c],t[c]=r,t[e]=o)}),t.forEach(function(n){var t,e,o=r(n),f=o[0],c=o[1];if(t=u[f])if(delete u[t.end],t.push(n),t.end=c,e=i[c]){delete i[e.start];var a=e===t?t:t.concat(e);i[a.start=t.start]=u[a.end=e.end]=a}else i[t.start]=u[t.end]=t;else if(t=i[c])if(delete i[t.start],t.unshift(n),t.start=f,e=u[f]){delete u[e.end];var s=e===t?t:e.concat(t);i[s.start=e.start]=u[s.end=t.end]=s}else i[t.start]=u[t.end]=t;else t=[n],i[t.start=f]=u[t.end=c]=t}),e(u,i),e(i,u),t.forEach(function(n){o[0>n?~n:n]||f.push([n])}),f}function r(n,r,e){function o(n){var t=0>n?~n:n;(s[t]||(s[t]=[])).push({i:n,g:a})}function i(n){n.forEach(o)}function u(n){n.forEach(i)}function f(n){"GeometryCollection"===n.type?n.geometries.forEach(f):n.type in l&&(a=n,l[n.type](n.arcs))}var c=[];if(arguments.length>1){var a,s=[],l={LineString:i,MultiLineString:u,Polygon:u,MultiPolygon:function(n){n.forEach(u)}};f(r),s.forEach(arguments.length<3?function(n){c.push(n[0].i)}:function(n){e(n[0].g,n[n.length-1].g)&&c.push(n[0].i)})}else for(var h=0,p=n.arcs.length;p>h;++h)c.push(h);return{type:"MultiLineString",arcs:t(n,c)}}function e(r,e){function o(n){n.forEach(function(t){t.forEach(function(t){(f[t=0>t?~t:t]||(f[t]=[])).push(n)})}),c.push(n)}function i(n){return l(u(r,{type:"Polygon",arcs:[n]}).coordinates[0])>0}var f={},c=[],a=[];return e.forEach(function(n){"Polygon"===n.type?o(n.arcs):"MultiPolygon"===n.type&&n.arcs.forEach(o)}),c.forEach(function(n){if(!n._){var t=[],r=[n];for(n._=1,a.push(t);n=r.pop();)t.push(n),n.forEach(function(n){n.forEach(function(n){f[0>n?~n:n].forEach(function(n){n._||(n._=1,r.push(n))})})})}}),c.forEach(function(n){delete n._}),{type:"MultiPolygon",arcs:a.map(function(e){var o=[];if(e.forEach(function(n){n.forEach(function(n){n.forEach(function(n){f[0>n?~n:n].length<2&&o.push(n)})})}),o=t(r,o),(n=o.length)>1)for(var u,c=i(e[0][0]),a=0;n>a;++a)if(c===i(o[a])){u=o[0],o[0]=o[a],o[a]=u;break}return o})}}function o(n,t){return"GeometryCollection"===t.type?{type:"FeatureCollection",features:t.geometries.map(function(t){return i(n,t)})}:i(n,t)}function i(n,t){var r={type:"Feature",id:t.id,properties:t.properties||{},geometry:u(n,t)};return null==t.id&&delete r.id,r}function u(n,t){function r(n,t){t.length&&t.pop();for(var r,e=s[0>n?~n:n],o=0,i=e.length;i>o;++o)t.push(r=e[o].slice()),a(r,o);0>n&&f(t,i)}function e(n){return n=n.slice(),a(n,0),n}function o(n){for(var t=[],e=0,o=n.length;o>e;++e)r(n[e],t);return t.length<2&&t.push(t[0].slice()),t}function i(n){for(var t=o(n);t.length<4;)t.push(t[0].slice());return t}function u(n){return n.map(i)}function c(n){var t=n.type;return"GeometryCollection"===t?{type:t,geometries:n.geometries.map(c)}:t in l?{type:t,coordinates:l[t](n)}:null}var a=v(n.transform),s=n.arcs,l={Point:function(n){return e(n.coordinates)},MultiPoint:function(n){return n.coordinates.map(e)},LineString:function(n){return o(n.arcs)},MultiLineString:function(n){return n.arcs.map(o)},Polygon:function(n){return u(n.arcs)},MultiPolygon:function(n){return n.arcs.map(u)}};return c(t)}function f(n,t){for(var r,e=n.length,o=e-t;o<--e;)r=n[o],n[o++]=n[e],n[e]=r}function c(n,t){for(var r=0,e=n.length;e>r;){var o=r+e>>>1;n[o]n&&(n=~n);var r=o[n];r?r.push(t):o[n]=[t]})}function r(n,r){n.forEach(function(n){t(n,r)})}function e(n,t){"GeometryCollection"===n.type?n.geometries.forEach(function(n){e(n,t)}):n.type in u&&u[n.type](n.arcs,t)}var o={},i=n.map(function(){return[]}),u={LineString:t,MultiLineString:r,Polygon:r,MultiPolygon:function(n,t){n.forEach(function(n){r(n,t)})}};n.forEach(e);for(var f in o)for(var a=o[f],s=a.length,l=0;s>l;++l)for(var h=l+1;s>h;++h){var p,g=a[l],v=a[h];(p=i[g])[f=c(p,v)]!==v&&p.splice(f,0,v),(p=i[v])[f=c(p,g)]!==g&&p.splice(f,0,g)}return i}function s(n,t){function r(n){i.remove(n),n[1][2]=t(n),i.push(n)}var e=v(n.transform),o=m(n.transform),i=g();return t||(t=h),n.arcs.forEach(function(n){for(var u,f,c=[],a=0,s=0,l=n.length;l>s;++s)f=n[s],e(n[s]=[f[0],f[1],1/0],s);for(var s=1,l=n.length-1;l>s;++s)u=n.slice(s-1,s+2),u[1][2]=t(u),c.push(u),i.push(u);for(var s=0,l=c.length;l>s;++s)u=c[s],u.previous=c[s-1],u.next=c[s+1];for(;u=i.pop();){var h=u.previous,p=u.next;u[1][2]0;){var r=(t+1>>1)-1,o=e[r];if(p(n,o)>=0)break;e[o._=t]=o,e[n._=t=r]=n}}function t(n,t){for(;;){var r=t+1<<1,i=r-1,u=t,f=e[u];if(o>i&&p(e[i],f)<0&&(f=e[u=i]),o>r&&p(e[r],f)<0&&(f=e[u=r]),u===t)break;e[f._=t]=f,e[n._=t=u]=n}}var r={},e=[],o=0;return r.push=function(t){return n(e[t._=o]=t,o++),o},r.pop=function(){if(!(0>=o)){var n,r=e[0];return--o>0&&(n=e[o],t(e[n._=0]=n,0)),r}},r.remove=function(r){var i,u=r._;if(e[u]===r)return u!==--o&&(i=e[o],(p(i,r)<0?n:t)(e[i._=u]=i,u)),u},r}function v(n){if(!n)return y;var t,r,e=n.scale[0],o=n.scale[1],i=n.translate[0],u=n.translate[1];return function(n,f){f||(t=r=0),n[0]=(t+=n[0])*e+i,n[1]=(r+=n[1])*o+u}}function m(n){if(!n)return y;var t,r,e=n.scale[0],o=n.scale[1],i=n.translate[0],u=n.translate[1];return function(n,f){f||(t=r=0);var c=(n[0]-i)/e|0,a=(n[1]-u)/o|0;n[0]=c-t,n[1]=a-r,t=c,r=a}}function y(){}var d={version:"1.6.18",mesh:function(n){return u(n,r.apply(this,arguments))},meshArcs:r,merge:function(n){return u(n,e.apply(this,arguments))},mergeArcs:e,feature:o,neighbors:a,presimplify:s};"function"==typeof define&&define.amd?define(d):"object"==typeof module&&module.exports?module.exports=d:this.topojson=d}(); -------------------------------------------------------------------------------- /offense_visualizer/visualizer.js: -------------------------------------------------------------------------------- 1 | var OFFENSE_TYPE = 2 | { 3 | 0:"Source IP", 4 | 1:"Destination IP", 5 | 2:"Event Name", 6 | 3:"Username", 7 | 4:"Source MAC Address", 8 | 5:"Destination MAC Address", 9 | 6:"Log Source", 10 | 7:"Hostname", 11 | 8:"Source Port", 12 | 9:"Destination Port", 13 | 10:"Source IPv6", 14 | 11:"Destination IPv6", 15 | 12:"Source ASN", 16 | 13:"Destination ASN", 17 | 14:"Rule", 18 | 15:"Application ID", 19 | 18:"Scheduled Search" 20 | }; 21 | 22 | function getValue( offense, attribute ) 23 | { 24 | if( attribute == "category_count" ) 25 | return offense.security_category_count + offense.policy_category_count; 26 | else if( attribute == "destination_count" ) 27 | return offense.local_destination_count + offense.remote_destination_count; 28 | else if( attribute == "start_time" || attribute == "last_updated_time" ) 29 | return offense[ attribute ]; 30 | else 31 | return offense[ attribute ]; 32 | } 33 | 34 | function reloadGraph() 35 | { 36 | var csrf = document.cookie.split("QRadarCSRF=").pop().split(";").shift(); 37 | var token = document.cookie.split("SEC=").pop().split(";").shift(); 38 | 39 | //Get offenses updated in the last 24 hours, maximum 100 results 40 | var xhr = d3.xhr( "/restapi/api/siem/offenses?filter=last_updated_time+%3E+" + ( ( new Date() ).getTime() - 86400000 ) , "application/json"); 41 | 42 | xhr.header('Accept', "application/json"); 43 | xhr.header('SEC', token); 44 | xhr.header('QRadarCSRF', csrf); 45 | xhr.header('Version', '4.0'); 46 | xhr.header('Range', 'items=1-100'); 47 | 48 | xhr.get( 49 | function (error,response) 50 | { 51 | var offenses = JSON.parse(response.responseText); 52 | var xAttribute = document.getElementById("xAxis").value; 53 | var yAttribute = document.getElementById("yAxis").value; 54 | var zAttribute = document.getElementById("zAxis").value; 55 | var cAttribute = document.getElementById("cAxis").value; 56 | 57 | var bubbleData = []; 58 | var shapes = ['circle', 'cross', 'triangle-up', 'triangle-down', 'diamond', 'square']; 59 | var random = d3.random.normal(); 60 | 61 | var offensesByType = {}; 62 | 63 | var startTime = [Number.MAX_VALUE,0]; 64 | var lastUpdated = [Number.MAX_VALUE,0]; 65 | var offenseMap = {}; 66 | 67 | // Added for OSM 68 | var offenseIpMap = []; 69 | document.getElementById('mapTab').innerHTML = "
"; 70 | var map = L.map('map'); 71 | map.locate({ setView: true, maxZoom:2 }); 72 | 73 | L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { 74 | attribution: '© OpenStreetMap contributors' 75 | }).addTo(map); 76 | 77 | for (i = 0; i < offenses.length; ++i) { 78 | var offense = offenses[i]; 79 | var typeValue = ( cAttribute == "offense_type" ? offense[ cAttribute ] + "" : offense.categories[0] ); 80 | 81 | typeOffenses = offensesByType[ typeValue ]; 82 | if( !typeOffenses ) 83 | { 84 | typeOffenses = []; 85 | offensesByType[ typeValue ] = typeOffenses; 86 | 87 | bubbleData.push({ 88 | key: cAttribute == "offense_type" ? OFFENSE_TYPE[ typeValue ] : typeValue, 89 | values: typeOffenses 90 | }); 91 | } 92 | 93 | typeOffenses.push({ 94 | x: getValue( offense, xAttribute ), 95 | y: getValue( offense, yAttribute ), 96 | size:getValue( offense, zAttribute ), 97 | }); 98 | 99 | var s = typeValue; 100 | s = s.replace(/[^\w]/g,"_"); 101 | 102 | offenseMap[ getValue( offense, xAttribute ) + "_" + getValue( offense, yAttribute ) + "_" + s ] = offense; 103 | // Added for OSM 104 | offenseIpMap[ getValue( offense, "offense_source" ) ] = offense; 105 | 106 | startTime = [ Math.min( startTime[0], offense.start_time ), Math.max( startTime[1], offense.startTime ) ]; 107 | lastUpdated = [ Math.min( lastUpdated[0], offense.last_updated_time ), Math.max( lastUpdated[1], offense.last_updated_time ) ]; 108 | } 109 | 110 | var pieData = []; 111 | for( typeValue in offensesByType ) { 112 | var typeOffenses = offensesByType[ typeValue ]; 113 | pieData.push( { label:(cAttribute == "offense_type" ? OFFENSE_TYPE[ typeValue ] : typeValue), value: typeOffenses.length } ); 114 | } 115 | 116 | nv.addGraph(function() { 117 | var chart = nv.models.scatterChart() 118 | .showDistX(true) 119 | .showDistY(true) 120 | .useVoronoi(true) 121 | .transitionDuration(500) 122 | .fisheye(1) 123 | .sizeRange([200,1500]); 124 | ; 125 | 126 | chart.tooltipContent(function(key, x, y, e, graph) { 127 | var s = e.series.key; 128 | if(typeof(s) == 'Date' ) 129 | s = s.getTime(); 130 | s = s.replace(/[^\w]/g,"_"); 131 | 132 | var x = e.point.x; 133 | if(typeof(x) == 'Date' ) 134 | x = sx.getTime(); 135 | 136 | var y = e.point.y; 137 | if(typeof(y) == 'Date' ) 138 | y = y.getTime(); 139 | 140 | var offense = offenseMap[ x + "_" + y + "_" + s ]; 141 | 142 | return '

Offense ' + 143 | offense.id + ' - ' + offense.description.replace("\n", "
  ") + 144 | "
" + 145 | "" + 146 | "" + 147 | "" + 148 | "
Magnitude:" + offense.magnitude + "
Credibility:" + offense.credibility + "
Severity:" + offense.severity + "
Relevance:" + offense.relevance + "

"; 149 | }); 150 | 151 | var attrs = {"xAttribute":xAttribute,"yAttribute":yAttribute}; 152 | for( attName in attrs ) 153 | { 154 | var attVal = attrs[attName]; 155 | if( attVal == "start_time" || attVal == "last_updated_time" ) 156 | { 157 | var x; 158 | if( attVal == "start_time" ) 159 | { 160 | x = d3.time.scale() 161 | .domain( [new Date( startTime[0] ), d3.time.hour.offset(new Date( startTime[1] ), 1)] ) 162 | .rangeRound([0, 800]); 163 | } 164 | else 165 | { 166 | x = d3.time.scale() 167 | .domain( [new Date( lastUpdated[0] ), d3.time.hour.offset(new Date( lastUpdated[1] ), 1)] ) 168 | .rangeRound([0, 800]); 169 | } 170 | 171 | var axis = attName == "xAttribute" ? chart.xAxis : chart.yAxis; 172 | axis.scale(x) 173 | .orient('bottom') 174 | .ticks(d3.time.days, 1) 175 | .tickFormat(function(v){var f=d3.time.format('%b %d %H:%M:%S');return f(new Date(v));}) 176 | .tickSize(10) 177 | .tickPadding(20); 178 | } 179 | } 180 | 181 | chart.scatter.dispatch.on("elementClick", function(e) { 182 | var s = e.series.key; 183 | if(typeof(s) == 'Date' ) 184 | s = s.getTime(); 185 | s = s.replace(/[^\w]/g,"_"); 186 | var x = e.point.x; 187 | if(typeof(x) == 'Date' ) 188 | x = sx.getTime(); 189 | var y = e.point.y; 190 | if(typeof(y) == 'Date' ) 191 | y = y.getTime(); 192 | var offense = offenseMap[ x + "_" + y + "_" + s ]; 193 | window.open( "/console/do/sem/offensesummary?summaryId=" + offense.id + "&appName=SEM&pageId=OffenseSummary" ); 194 | }); 195 | 196 | d3.select('#graph svg').datum(bubbleData).call(chart); 197 | 198 | return chart; 199 | }); 200 | 201 | nv.addGraph(function() { 202 | var chart = nv.models.pieChart() 203 | .x(function(d) { return d.label }) 204 | .y(function(d) { return d.value }) 205 | .showLabels(false) 206 | .labelThreshold(.05) 207 | .labelType("percent") 208 | .donut(true) 209 | .donutRatio(0.35) 210 | ; 211 | 212 | d3.select("#piechart svg") 213 | .datum(pieData) 214 | .transition().duration(350) 215 | .call(chart); 216 | 217 | return chart; 218 | }); 219 | 220 | // Added for OSM 221 | for( geoip in offenseIpMap ) { 222 | var ip = getValue (offenseIpMap[geoip], "offense_source"); 223 | 224 | if( ip.match(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/) ) { 225 | // console.log( offenseIpMap[geoip] ); 226 | d3.json('https://freegeoip.net/json/' + ip, 227 | function (error,data) { 228 | if (typeof data !== 'undefined') { 229 | console.log(data); 230 | 231 | var offense = offenseIpMap[data.ip]; 232 | console.log(offense); 233 | 234 | L.marker([data.latitude, data.longitude]).addTo(map) 235 | .bindPopup('

Offense ' + 236 | '' + offense.id + 238 | ' - ' + offense.description.replace("\n", "
  ") + 239 | "
" + 240 | "" + 241 | "" + 242 | "" + 243 | "
Magnitude:" + offense.magnitude + "
Credibility:" + offense.credibility + "
Severity:" + offense.severity + "
Relevance:" + offense.relevance + "

"); 244 | } 245 | else { 246 | // console.log("response is 'undefined'"); 247 | } 248 | } 249 | ); 250 | } 251 | } 252 | } 253 | ); 254 | } 255 | -------------------------------------------------------------------------------- /incident_overview/README.md: -------------------------------------------------------------------------------- 1 | # NOW AVAILABLE ON THE IBM SECURITY APP EXCHANGE 2 | 3 | The [Incident Overview App](https://exchange.xforce.ibmcloud.com/hub/extension/IBMQRadar:IncidentOverview "Incident Overview App") is now available as an app via the [IBM Security App Exchange] (https://exchange.xforce.ibmcloud.com/hub)! 4 | 5 | 6 | # ABOUT 7 | This tool allows you to visualize all of the incidents (offenses) on your QRadar installation. Each bubble represents an incident. The size and color of the bubble indicates the magnitude of the incident. Lines dawn between the bubbles indicate there are shared IP addresses among the linked incidents. 8 | 9 | Clicking on an incident bubble allows you to see details of that incident at a glance. 10 | 11 | **This project requires APIs only available in QRadar v7.2.5 and later** 12 | 13 | ![Alt text](screenshot.png "Incident Overview Screenshot") 14 | 15 | # RUNNING THE QRADAR INCIDENT OVERVIEW 16 | 17 | This project installation has Zero installation footprint on QRadar and lives on another host (separate OS/webserver/DB) 18 | 19 | The installation contains three major steps 20 | 21 | 1. Creating & Configuring a new host environment 22 | 2. Configuring & Building the visualization app 23 | 3. Configuring QRadar to talk to the visualization app 24 | 25 | # CONFIGURING HOST ENVIRONMENT 26 | This app can handily run in a VM. Consider using vSphere or VMWare Fusion. 27 | 28 | ## INSTALL UBUNTU 29 | Download and install 14.04 / 14.10 30 | 31 | If you're running on a VM, be sure and allocate the following hardware specs: 32 | * Hardware 33 | * HD Capacity: 50GB 34 | * Memory: 4GB Ram 35 | 36 | **PROCEED AS ROOT** 37 | Most of the following steps require you to be logged into Ubuntu as root. 38 | ```sh 39 | sudo su 40 | ``` 41 | 42 | ## INSTALL GRADLE 43 | ```sh 44 | sudo apt-get update 45 | sudo apt-get install gradle 46 | ``` 47 | ## INSTALL POSTGRES 48 | Install Postgres 9.2.1 (or latest: 9.4) 49 | 50 | ```sh 51 | sudo apt-get install postgresql postgresql-contrib 52 | ``` 53 | 54 | ### User setup and Password 55 | See: http://suite.opengeo.org/opengeo-docs/dataadmin/pgGettingStarted/firstconnect.html 56 | 57 | To start off, we need to change the 'postgres' user password; we will not be able to access the server otherwise. As the “postgres” Linux user, we will execute the psql command. 58 | 59 | ```sh 60 | sudo -u postgres psql postgres 61 | ``` 62 | 63 | Set a password for the "postgres" database role using the command: 64 | 65 | ```sh 66 | \password postgres 67 | ``` 68 | 69 | Set the NEW password to 'postgres' (or whatever you like. Remember this password). 70 | 71 | ```sh 72 | \q to exit psql 73 | ``` 74 | 75 | Also update pg_hba.conf to allow peer connections. 76 | 77 | ```sh 78 | sudo vi /etc/postgresql/9.4/main/pg_hba.conf 79 | ``` 80 | 81 | Change 127.0.0.1 to all 82 | 83 | ### Restart postgres 84 | ```sh 85 | service postgresql restart 86 | ``` 87 | 88 | The steps required to configure the database to store the data for this application will be coved in the "Create Visualization Database & Tables" section later in this guide. 89 | 90 | ## INSTALL JAVA 7 91 | 92 | ```sh 93 | sudo apt-get install openjdk-7-jdk 94 | ``` 95 | 96 | 97 | ## INSTALL LIBERTY 8.5.5.4 98 | 99 | Download two jars 100 | 101 | wlp-developers-runtime.8.5.5.4.jar 102 | https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/wasdev/downloads/wlp/8.5.5.4/wlp-developers-runtime-8.5.5.4.jar 103 | 104 | wlp-developers-extended.8.5.5.4.jar 105 | https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/wasdev/downloads/wlp/8.5.5.4/wlp-developers-extended-8.5.5.4.jar 106 | 107 | 108 | Move the jars to you environment / VM and Extract them 109 | ```sh 110 | mkdir /opt/wlp/ 111 | ``` 112 | 113 | Extract to: /opt/wlp 114 | Update directory permissions as needed 115 | 116 | ```sh 117 | java -jar wlp-developers-runtime.8.5.5.4.jar 118 | java -jar wlp-developers-extended.8.5.5.4.jar 119 | ``` 120 | 121 | Create a new server for the visualization 122 | 123 | ```sh 124 | /opt/wlp/bin/server create 125 | ``` 126 | 127 | This will create a new server called 'defaultServer' under /opt/wlp/usr/servers/ 128 | 129 | Start Liberty to verify setup 130 | ```sh 131 | sudo chmod +777 /opt/ 132 | ``` 133 | 134 | Check and Open firewall ports (if necessary) 135 | 136 | ```sh 137 | ufw enable [checks firewall is up] 138 | ufw allow 9080 [opens firewall] 139 | ``` 140 | 141 | Start the server to verify 142 | ```sh 143 | /opt/wlp/bin/server run 144 | ``` 145 | 146 | Open a browser and go to localhost:9080 (default liberty port). You should see a 'Welcome to Liberty' page. 147 | 148 | ## Install JDBC41 Driver 149 | Download the 4.1 postgres driver from https://jdbc.postgresql.org/ to /opt/wlp/lib/ 150 | ```sh 151 | cd /opt/wlp/lib 152 | wget https://jdbc.postgresql.org/download/postgresql-9.4-1201.jdbc41.jar 153 | ``` 154 | After downloading the app (in the next step), Ensure src/main/etc/server.xml refers to the proper name and location of this JDBC driver. 155 | 156 | 157 | # CONFIGURING THE APPLICATION 158 | 159 | Download Visualization Application Source Code 160 | Get project from GitHub https://github.com/ibm-security-intelligence/visualizations 161 | Move it to your host in a convenient directory - /visSource/ 162 | 163 | ## Create Visualization Database & Tables 164 | Create the offenseviz DB 165 | 166 | ```sh 167 | CREATE DATABASE offenseviz 168 | ``` 169 | 170 | Create the required tables to store the application data 171 | 172 | ```sh 173 | psql -U postgres -d -W -h localhost offenseviz -a -f /visSource/src/main/sql/create-db.sql 174 | ``` 175 | 176 | ## GEOIP LOOKUP LIBARARY 177 | This project makes use to GeoIP lookups to plot the location of IP addresses on a map. You'll need to get the GeoIP data from MaxMind. 178 | 179 | Visit http://dev.maxmind.com/geoip/geoip2/geolite2/ and download GeoLite2 City http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz 180 | 181 | ```sh 182 | wget http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz 183 | ``` 184 | 185 | The visualization will load the GeoLite database from /opt/ so, 186 | 187 | ```sh 188 | gunzip GeoLite2-City.mmdb.gz 189 | mv GeoLite2-City.mmdb /opt/ 190 | ``` 191 | 192 | ## CONFIGURE VISUALIZATION SETTINGS 193 | The visualization itself is configured via one file: qradar.properties. Before you build the offense-visualization WAR, you'll need to edit this file to your particular needs. Navigate to directory where you downloaded the visualization source code (/visSource). Now, edit 194 | 195 | ```sh 196 | cd /visSource 197 | src/main/resources/qradar.properties 198 | ``` 199 | 200 | auth_token 201 | : The long authorization token from the 'Auth Token' step below 202 | 203 | url 204 | : The URL or address of your QRadar console 205 | 206 | qradar_timezone 207 | : The ISO timezone of your console 208 | 209 | update_interval_seconds 210 | : how frequently you want the visualization to look for new offenses in QRadar 211 | 212 | default_latitude, default longitude 213 | : If the visualization can't determine the geographic location of an IP (such as IPs internal to your network), this is where these IPs will display on the map. Consider setting this to the lat/long of your current physical location, main office or HQ. 214 | 215 | default_country, default_city 216 | : Similarly to the default lat / log. If the visualization can't find a location for an IP, these are the details that will be returned. They should describe the default lat / long. 217 | 218 | cleanup_old_offense_interval 219 | : The visualization watches offenses over time. Each time it gets a new snapshot of offenses, this is an 'interval'. The UI typically displays 10 - 20 intervals after which the offenses can be scrubbed from the DB as they're no longer needed. 220 | 221 | ## BUILD THE APPLICATION 222 | You need to compile the source code into a war file for Liberty to serve. 223 | 224 | ```sh 225 | gradle build 226 | ``` 227 | 228 | This will create a war file in build/libs/incident-overview.war 229 | 230 | 231 | ## INSTALL THE VISUALIZATION TO LIBERTY 232 | Drop the built war into the Liberty Apps folder 233 | 234 | ```sh 235 | cp /visSource/build/libs/offense-viz.war /opt/wlp/usr/servers/defaultServer/apps/ 236 | ``` 237 | 238 | # CONFIGURE CONNECTION TO QRADAR 239 | The application needs a QRadar box to run on to fetch offense data. 240 | 241 | ## CORS Authorization 242 | By default, the web server running Tomcat won't accept REST API requests from any IPs other that local IPs. Since we need the visualization to have access to some of these APIs, we need to allow CORS (Cross Origin Resource Sharing). 243 | 244 | Via a terminal, log into your QRadar console as root and edit 245 | 246 | ```sh 247 | vi /opt/qradar/webapps/console/restapi/allowed_origins.list 248 | ``` 249 | 250 | Add a '*' (without the quotes) to the last empty line (ensure the line doesn't start with '#'). 251 | 252 | This will allow *all* authenticated web browsers to query the REST API on QRadar. 253 | 254 | The file is read only, so to save, use 'w!'. No need to restart tomcat! 255 | 256 | ## Auth Token 257 | The visualization needs a unique authorization token to identify itself to QRadar. We do this via an Authorized Services token. 258 | 259 | * Using a web browser, Log into your QRadar console as an admin. 260 | * Navigate to the admin section and select "Authorized Services". 261 | * Select 'Add Authorized Service'. 262 | * For the name, choose something clear such as 'Offense Visualization' 263 | * For the Role and Security Profile, choose the options which match the offenses you wish to display in the visualization. 264 | * Check 'No Expiry' 265 | * Select the "Create Services" button 266 | 267 | A new row will appear in the table. Copy the long string of characters in the 'Authentication Token' which you just created. You'll need it again in a minute. 268 | 269 | 270 | 271 | ## STARTING THE VISUALIZATION 272 | If all is set up, you can now start the Liberty application Server. 273 | 274 | ```sh 275 | /opt/wlp/bin/server run 276 | ``` 277 | 278 | If all is well, open your favorite browser and navigate to 279 | 280 | ``` 281 | http://{$servername}:9080/offense-viz 282 | ``` 283 | 284 | and enjoy your new QRadar Security Incident Overview! 285 | 286 | 287 | NOTICES 288 | This product includes GeoLite2 data created by MaxMind, available from http://www.maxmind.com 289 | -------------------------------------------------------------------------------- /incident_overview/src/main/webapp/css/vis.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Eric Meyer's Reset CSS v2.0 (http://meyerweb.com/eric/tools/css/reset/) 3 | * http://cssreset.com 4 | */ 5 | html, body, div, span, applet, object, iframe, 6 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 7 | a, abbr, acronym, address, big, cite, code, 8 | del, dfn, em, img, ins, kbd, q, s, samp, 9 | small, strike, strong, sub, sup, tt, var, 10 | b, u, i, center, 11 | dl, dt, dd, ol, ul, li, 12 | fieldset, form, label, legend, 13 | table, caption, tbody, tfoot, thead, tr, th, td, 14 | article, aside, canvas, details, embed, 15 | figure, figcaption, footer, header, hgroup, 16 | menu, nav, output, ruby, section, summary, 17 | time, mark, audio, video { 18 | margin: 0; 19 | padding: 0; 20 | border: 0; 21 | font-size: 100%; 22 | font: inherit; 23 | vertical-align: baseline; 24 | } 25 | /* HTML5 display-role reset for older browsers */ 26 | article, aside, details, figcaption, figure, 27 | footer, header, hgroup, menu, nav, section { 28 | display: block; 29 | } 30 | body { 31 | line-height: 1; 32 | } 33 | ol, ul { 34 | list-style: none; 35 | margin-bottom: 5px; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | /************ END RESET ****************/ 50 | 51 | /**** TIPSY OVERRIDE ***/ 52 | .tipsy { font-size: 16px !important;} 53 | /**** NO DATA Alignment fix ****/ 54 | 55 | ::-webkit-scrollbar 56 | { 57 | width: 12px; /* for vertical scrollbars */ 58 | height: 12px; /* for horizontal scrollbars */ 59 | } 60 | 61 | ::-webkit-scrollbar-track 62 | { 63 | background: rgba(0, 0, 0, 0.1); 64 | } 65 | 66 | ::-webkit-scrollbar-thumb 67 | { 68 | background: rgba(0, 0, 0, 0.5); 69 | } 70 | 71 | #visTitle 72 | { 73 | margin-left: 10px; 74 | margin-top: 10px; 75 | font-size: 30px; 76 | } 77 | 78 | #titlebar 79 | { 80 | margin-top: 10px; 81 | background-color:#1A2830; 82 | color: #FFFFFF; 83 | height: 60px; 84 | } 85 | 86 | .producttitle 87 | { 88 | width: auto; 89 | padding-left: 20px; 90 | padding-right: 40px; 91 | 92 | padding-top: 24px; 93 | 94 | margin: 0; 95 | font-size: 12px; 96 | font-weight: lighter; 97 | letter-spacing: .1em; 98 | text-transform: uppercase; 99 | background-color: #1A2830; 100 | white-space: nowrap; 101 | } 102 | 103 | .producttitle a 104 | { 105 | cursor: pointer; 106 | text-decoration: none; 107 | color: #fafafa; 108 | padding-bottom: 0; 109 | } 110 | 111 | #flourishTrack 112 | { 113 | border-width: 1px 0 0 0; 114 | border-color: black; 115 | border-style: solid; 116 | } 117 | 118 | #flourishTrack #flourish 119 | { 120 | top: -1px; 121 | } 122 | 123 | /******************************************* 124 | * RIGHT HAND SIDE CONTENT 125 | *********************************************/ 126 | .feedBarContainer 127 | { 128 | position: absolute; 129 | right: 0; 130 | top: 0; 131 | 132 | /*background: 0 0 repeat, #E8E8E8;*/ 133 | height: 100%; 134 | 135 | overflow-y: auto; 136 | over w-x: hidden; 137 | 138 | float: right; 139 | width: 275px; 140 | 141 | /*min-width:100px;*/ 142 | 143 | /*background: url(https://tisp.stage1.mybluemix.net/images/cross-pattern-lg.ae01f735.png) 0 0/160px 160px repeat,#f7f7f7;*/ 144 | background-color: #eee; 145 | background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0980392) 0px, rgba(0, 0, 0, 0) 6px, transparent 0px); 146 | } 147 | 148 | .feedBarContent 149 | { 150 | padding: 10px; 151 | display: inline-block; 152 | width: 90%; 153 | } 154 | 155 | .smallChart 156 | { 157 | margin-bottom: 20px; 158 | padding: 10px; 159 | } 160 | 161 | .smallChart H1 162 | { 163 | font-size: 14pt; 164 | color: #000000; 165 | border-bottom: 1px; 166 | border-color: black; 167 | border-style: solid; 168 | margin-bottom: 5px; 169 | } 170 | 171 | /******** OFFENSE VIS **************/ 172 | #offenseReport 173 | { 174 | background-color: #FEFEFE; 175 | 176 | z-index:100; 177 | position:absolute; 178 | right:-1030px; 179 | 180 | box-shadow: -5px 0px 5px 0px #888888; 181 | 182 | overflow-y: auto; 183 | 184 | height: 100%; 185 | 186 | width: 99%; 187 | max-width: 750px; 188 | } 189 | #offenseReport a 190 | { 191 | color: #286090; 192 | } 193 | 194 | #assignedToUser 195 | { 196 | width: 96px; 197 | position: absolute; 198 | right: 5px; 199 | top: 60px; 200 | } 201 | 202 | #userName 203 | { 204 | text-align: center; 205 | } 206 | .userNameAssigned 207 | { 208 | color: #000000; 209 | } 210 | .userNameUnassigned 211 | { 212 | color: #AAAAAA; 213 | } 214 | 215 | 216 | .discreteBar 217 | { 218 | cursor: pointer; 219 | } 220 | 221 | #offenseCategories 222 | { 223 | max-height: 200px; 224 | overflow: auto; 225 | 226 | } 227 | 228 | .offenseReportContents 229 | { 230 | padding-bottom: 4px; 231 | } 232 | 233 | .offenseReportContentsRow 234 | { 235 | padding-left: 10px; 236 | padding-right: 10px; 237 | } 238 | 239 | .offenseReportContentsRow th 240 | { 241 | text-align: left; 242 | font-weight: 700; 243 | padding-bottom: 10px; 244 | padding-right: 10px; 245 | width: 130px; 246 | } 247 | 248 | .offenseReportContentsRow li 249 | { 250 | padding-bottom: 8px; 251 | } 252 | 253 | .offenseReportContentsHeader 254 | { 255 | position: relative; 256 | min-height: 100px; 257 | margin-left: 108px; 258 | } 259 | 260 | .offenseReportContents .paneTitle 261 | { 262 | font-size: 10pt; 263 | color: #AAAAAA; 264 | padding-top: 10px; 265 | } 266 | 267 | .offenseReportContents H1 268 | { 269 | margin-top: 10px; /* Don't overlap the 'investigate' button */ 270 | bottom: 0; 271 | font-size: 18pt; 272 | font-weight: bold; 273 | margin-bottom: 5px; 274 | max-width: 82%; 275 | } 276 | 277 | .offenseReportContents H2 278 | { 279 | font-size: 16pt; 280 | border-bottom: 2px solid #ccc; 281 | margin-top: 10px; 282 | margin-bottom: 5px; 283 | } 284 | 285 | .offenseReportContents .magnitudeContainer 286 | { 287 | float: left; 288 | width: 100px; 289 | height: 100px; 290 | 291 | margin-right: 8px; 292 | margin-bottom: 8px; 293 | 294 | background-color: #CCCCCC; 295 | 296 | color: white; 297 | 298 | text-align: center; 299 | vertical-align: middle; 300 | 301 | border-bottom: 5px solid #888888; 302 | } 303 | .magnitudeText 304 | { 305 | padding-top: 7px; 306 | display: block; 307 | } 308 | 309 | .magnitude 310 | { 311 | font-size: 36pt; 312 | display: block; 313 | padding-top: 14px; 314 | } 315 | 316 | dl 317 | { 318 | padding: 0.5em; 319 | } 320 | 321 | dt 322 | { 323 | float: left; 324 | clear: left; 325 | width: 150px; 326 | text-align: right; 327 | vertical-align: top; 328 | font-weight: bold; 329 | } 330 | 331 | dd 332 | { 333 | margin: 0 0 0 10px; 334 | padding: 0 0 0.5em 0; 335 | } 336 | 337 | #OffenseNotes 338 | { 339 | max-height: 100px; 340 | overflow: auto; 341 | } 342 | 343 | #OffenseNotes ol li 344 | { 345 | margin-bottom: 10px; 346 | } 347 | 348 | .offenseNoteAuthor 349 | { 350 | font-size: 10pt; 351 | color: #AAAAAA; 352 | margin-right: 5px; 353 | font-weight: bold; 354 | } 355 | .offenseNoteDate 356 | { 357 | font-size: 10pt; 358 | color: #AAAAAA; 359 | margin-left: 5px; 360 | } 361 | /******** END OFFENSE VIS **************/ 362 | 363 | 364 | 365 | 366 | body 367 | { 368 | position: fixed; 369 | background-color: #41484D; 370 | font-family: Helvetica,Arial,sans-serif; 371 | height: 100%; 372 | width: 100%; 373 | overflow: hidden; 374 | } 375 | 376 | html 377 | { 378 | height: 100%; 379 | } 380 | 381 | #mainContentPane 382 | { 383 | background-color: #FFFFFF; 384 | margin:20px; 385 | position: relative; 386 | /*height:75%;*/ 387 | height: calc(100% - 150px); 388 | } 389 | 390 | 391 | #newsTickerDiv 392 | { 393 | margin-bottom: 10px; 394 | background-color:#1A2830; 395 | color: #FFFFFF; 396 | height: 30px; 397 | 398 | padding-left: 20px; 399 | padding-top: 12px; 400 | overflow: hidden; 401 | 402 | } 403 | 404 | #newsTickerDiv H1 405 | { 406 | color: #FFFFFF; 407 | font-weight:bold; 408 | display: inline; 409 | float: left; 410 | margin-right: 5px; 411 | } 412 | 413 | .newsTickerFeed 414 | { 415 | overflow: hidden; 416 | } 417 | 418 | .newsTickerFeed li 419 | { 420 | height: 60px; 421 | } 422 | .newsTickerFeed li a 423 | { 424 | color: #FFFFFF; 425 | text-decoration: none; 426 | } 427 | 428 | #timeSeriesWrapper 429 | { 430 | width: calc(100% - 267px); 431 | left: 0px; 432 | } 433 | 434 | #timeSlider 435 | { 436 | width: calc(100% - 345px); 437 | left: 35px; 438 | margin: 0 auto; /*Center the time slider in the parent div */ 439 | 440 | } 441 | 442 | #timeSlider > svg { 443 | height: 50px; 444 | } 445 | 446 | .tooltip 447 | { 448 | z-index: 1; 449 | position: absolute; 450 | width: auto; 451 | height: auto; 452 | padding: 6px; 453 | background-color: white; 454 | opacity: 1; 455 | -webkit-border-radius: 10px; 456 | -moz-border-radius: 10px; 457 | border-radius: 10px; 458 | -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); 459 | -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); 460 | box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); 461 | pointer-events: none; 462 | } 463 | 464 | .tooltip.hidden 465 | { 466 | display: none; 467 | } 468 | 469 | /* clean up the nvd3 time sers graph on the bootom of the vis */ 470 | #timeSeries > svg > g > g > g.nv-linesWrap > rect 471 | { 472 | display:none; 473 | } 474 | 475 | .button 476 | { 477 | display: inline-block; 478 | padding: 6px 12px; 479 | margin-bottom: 0; 480 | font-size: 14px; 481 | font-weight: 400; 482 | line-height: 1.42857143; 483 | text-align: center; 484 | white-space: nowrap; 485 | vertical-align: middle; 486 | -ms-touch-action: manipulation; 487 | touch-action: manipulation; 488 | cursor: pointer; 489 | -webkit-user-select: none; 490 | -moz-user-select: none; 491 | -ms-user-select: none; 492 | user-select: none; 493 | background-image: none; 494 | border: 1px solid transparent; 495 | border-radius: 4px; 496 | 497 | color: #fff; 498 | background-color: #286090; 499 | border-color: #204d74; 500 | } 501 | 502 | .investigateButton 503 | { 504 | position: absolute; 505 | right: 10px; 506 | top: 10px; 507 | z-index: 1; 508 | } 509 | 510 | .clear { 511 | clear: both; 512 | } 513 | 514 | /* Ensure the data point markers are always visible on the time sersies chart */ 515 | .nv-point { 516 | stroke-opacity: 1!important; 517 | stroke-width: 5px!important; 518 | fill-opacity: 1!important; 519 | } 520 | 521 | .OffenseText, #chart1vis .nv-slice { 522 | cursor: pointer; 523 | } 524 | -------------------------------------------------------------------------------- /incident_overview/src/main/webapp/index.jsp: -------------------------------------------------------------------------------- 1 | 12 | 13 | <%@ page import="com.ibm.si.qradar.offenseviz.conf.QRadarConfig" %> 14 | <%@ page import="java.util.TimeZone" %> 15 | <%@ page import="java.util.Date" %> 16 | 17 | 18 | 19 | 20 | IBM QRadar Security Intelligence 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | <% 40 | QRadarConfig qConfig = QRadarConfig.getInstance(); 41 | String server = qConfig.getUrl(); 42 | String token = qConfig.getAuthorizationToken(); 43 | 44 | String xForceToken = qConfig.getXforceAuthToken(); 45 | 46 | String xForceApiUrl = qConfig.getXforceApiUrl(); 47 | String xForceWebUrl = qConfig.getXforceWebUrl(); 48 | 49 | int updateIntervalInSeconds = qConfig.getUpdateInterval(); 50 | 51 | final TimeZone timeZone = TimeZone.getTimeZone(qConfig.getQradarTimeZone()); 52 | int offset = timeZone.getOffset( System.currentTimeMillis() ); 53 | %> 54 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 161 | 162 | 163 | 164 | 167 |
168 | 169 |
170 | 171 |

172 | 173 | 174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 | 182 |
183 |
184 |
185 | 186 |
187 |

Incidents by Magnitude

188 |
189 |
190 | 191 |
192 |

Average Incident Duration

193 |
194 |
0
195 |
Minutes
196 |
197 |
198 | 199 |
200 |

Top Categories

201 |
202 |
203 | 204 |
205 |

Top Incidents by Event Count

206 |
207 |
208 | 209 |
210 |
211 | 212 |
213 | Investigate > 214 |
215 | 216 |
Rick McCaskill
217 |
218 |
219 |
220 |
221 | Magnitude 222 | 10 223 |
224 |
225 |
QRadar Incident Report
226 |

Offense Title

227 |
228 |
229 | 230 |
231 |
232 |

Details

233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 266 | 267 |
Categories
Duration
Incident Type
Notes 250 |
    251 |
  1. 252 |
253 |
Linked Incidents
Events / Flows 262 | 0 Events 263 | / 264 | 0 Flows 265 |
268 | 269 | 270 |

Incident Contributors

271 |
272 | 285 |
286 | 287 |

IP Relationships

288 |
289 |
290 |
291 |
292 | 293 |
294 | 295 | 298 | 301 | 302 |
303 |

Security Intelligence News |

304 | 305 |
306 |
    307 |
308 |
309 |
310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /incident_overview/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /incident_overview/src/main/webapp/map/js/jquery.tipsy.js: -------------------------------------------------------------------------------- 1 | // tipsy, facebook style tooltips for jquery 2 | // version 1.0.0a 3 | // (c) 2008-2010 jason frame [jason@onehackoranother.com] 4 | // released under the MIT license 5 | 6 | (function($) { 7 | 8 | function maybeCall(thing, ctx) { 9 | return (typeof thing == 'function') ? (thing.call(ctx)) : thing; 10 | } 11 | 12 | function Tipsy(element, options) { 13 | this.$element = $(element); 14 | this.options = options; 15 | this.enabled = true; 16 | this.fixTitle(); 17 | } 18 | 19 | Tipsy.prototype = { 20 | show: function() { 21 | var title = this.getTitle(); 22 | if (title && this.enabled) { 23 | var $tip = this.tip(); 24 | 25 | $tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title); 26 | $tip[0].className = 'tipsy'; // reset classname in case of dynamic gravity 27 | $tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).prependTo(document.body); 28 | 29 | var pos = $.extend({}, this.$element.offset(), { 30 | width: this.$element[0].offsetWidth || 0, 31 | height: this.$element[0].offsetHeight || 0 32 | }); 33 | 34 | if (typeof this.$element[0].nearestViewportElement == 'object') { 35 | // SVG 36 | var el = this.$element[0]; 37 | var rect = el.getBoundingClientRect(); 38 | pos.width = rect.width; 39 | pos.height = rect.height; 40 | } 41 | 42 | 43 | var actualWidth = $tip[0].offsetWidth, 44 | actualHeight = $tip[0].offsetHeight, 45 | gravity = maybeCall(this.options.gravity, this.$element[0]); 46 | 47 | var tp; 48 | switch (gravity.charAt(0)) { 49 | case 'n': 50 | tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}; 51 | break; 52 | case 's': 53 | tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}; 54 | break; 55 | case 'e': 56 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset}; 57 | break; 58 | case 'w': 59 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset}; 60 | break; 61 | } 62 | 63 | if (gravity.length == 2) { 64 | if (gravity.charAt(1) == 'w') { 65 | tp.left = pos.left + pos.width / 2 - 15; 66 | } else { 67 | tp.left = pos.left + pos.width / 2 - actualWidth + 15; 68 | } 69 | } 70 | 71 | $tip.css(tp).addClass('tipsy-' + gravity); 72 | $tip.find('.tipsy-arrow')[0].className = 'tipsy-arrow tipsy-arrow-' + gravity.charAt(0); 73 | if (this.options.className) { 74 | $tip.addClass(maybeCall(this.options.className, this.$element[0])); 75 | } 76 | 77 | if (this.options.fade) { 78 | $tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity}); 79 | } else { 80 | $tip.css({visibility: 'visible', opacity: this.options.opacity}); 81 | } 82 | 83 | var t = this; 84 | var set_hovered = function(set_hover){ 85 | return function(){ 86 | t.$tip.stop(); 87 | t.tipHovered = set_hover; 88 | if (!set_hover){ 89 | if (t.options.delayOut === 0) { 90 | t.hide(); 91 | } else { 92 | setTimeout(function() { 93 | if (t.hoverState == 'out') t.hide(); }, t.options.delayOut); 94 | } 95 | } 96 | }; 97 | }; 98 | $tip.hover(set_hovered(true), set_hovered(false)); 99 | } 100 | }, 101 | 102 | hide: function() { 103 | if (this.options.fade) { 104 | this.tip().stop().fadeOut(function() { $(this).remove(); }); 105 | } else { 106 | this.tip().remove(); 107 | } 108 | }, 109 | 110 | fixTitle: function() { 111 | var $e = this.$element; 112 | 113 | if ($e.attr('title') || typeof($e.attr('original-title')) != 'string') { 114 | $e.attr('original-title', $e.attr('title') || '').removeAttr('title'); 115 | } 116 | if (typeof $e.context.nearestViewportElement == 'object'){ 117 | if ($e.children('title').length){ 118 | $e.append('' + ($e.children('title').text() || '') + '') 119 | .children('title').remove(); 120 | } 121 | } 122 | }, 123 | 124 | getTitle: function() { 125 | 126 | var title, $e = this.$element, o = this.options; 127 | this.fixTitle(); 128 | 129 | if (typeof o.title == 'string') { 130 | var title_name = o.title == 'title' ? 'original-title' : o.title; 131 | if ($e.children(title_name).length){ 132 | title = $e.children(title_name).html(); 133 | } else{ 134 | title = $e.attr(title_name); 135 | } 136 | 137 | } else if (typeof o.title == 'function') { 138 | title = o.title.call($e[0]); 139 | } 140 | title = ('' + title).replace(/(^\s*|\s*$)/, ""); 141 | return title || o.fallback; 142 | }, 143 | 144 | tip: function() { 145 | if (!this.$tip) { 146 | this.$tip = $('
').html('
'); 147 | } 148 | return this.$tip; 149 | }, 150 | 151 | validate: function() { 152 | if (!this.$element[0].parentNode) { 153 | this.hide(); 154 | this.$element = null; 155 | this.options = null; 156 | } 157 | }, 158 | 159 | enable: function() { this.enabled = true; }, 160 | disable: function() { this.enabled = false; }, 161 | toggleEnabled: function() { this.enabled = !this.enabled; } 162 | }; 163 | 164 | $.fn.tipsy = function(options) { 165 | 166 | if (options === true) { 167 | return this.data('tipsy'); 168 | } else if (typeof options == 'string') { 169 | var tipsy = this.data('tipsy'); 170 | if (tipsy) tipsy[options](); 171 | return this; 172 | } 173 | 174 | options = $.extend({}, $.fn.tipsy.defaults, options); 175 | 176 | if (options.hoverlock && options.delayOut === 0) { 177 | options.delayOut = 100; 178 | } 179 | 180 | function get(ele) { 181 | var tipsy = $.data(ele, 'tipsy'); 182 | if (!tipsy) { 183 | tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options)); 184 | $.data(ele, 'tipsy', tipsy); 185 | } 186 | return tipsy; 187 | } 188 | 189 | function enter() { 190 | var tipsy = get(this); 191 | tipsy.hoverState = 'in'; 192 | if (options.delayIn === 0) { 193 | tipsy.show(); 194 | } else { 195 | tipsy.fixTitle(); 196 | setTimeout(function() { if (tipsy.hoverState == 'in') tipsy.show(); }, options.delayIn); 197 | } 198 | } 199 | 200 | function leave() { 201 | var tipsy = get(this); 202 | tipsy.hoverState = 'out'; 203 | if (options.delayOut === 0) { 204 | tipsy.hide(); 205 | } else { 206 | var to = function() { 207 | if (!tipsy.tipHovered || !options.hoverlock){ 208 | if (tipsy.hoverState == 'out') tipsy.hide(); 209 | } 210 | }; 211 | setTimeout(to, options.delayOut); 212 | } 213 | } 214 | 215 | if (options.trigger != 'manual') { 216 | var binder = options.live ? 'live' : 'bind', 217 | eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus', 218 | eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur'; 219 | this[binder](eventIn, enter)[binder](eventOut, leave); 220 | } 221 | 222 | return this; 223 | 224 | }; 225 | 226 | $.fn.tipsy.defaults = { 227 | className: null, 228 | delayIn: 0, 229 | delayOut: 0, 230 | fade: false, 231 | fallback: '', 232 | gravity: 'n', 233 | html: false, 234 | live: false, 235 | offset: 0, 236 | opacity: 0.8, 237 | title: 'title', 238 | trigger: 'hover', 239 | hoverlock: false 240 | }; 241 | 242 | // Overwrite this method to provide options on a per-element basis. 243 | // For example, you could store the gravity in a 'tipsy-gravity' attribute: 244 | // return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' }); 245 | // (remember - do not modify 'options' in place!) 246 | $.fn.tipsy.elementOptions = function(ele, options) { 247 | return $.metadata ? $.extend({}, options, $(ele).metadata()) : options; 248 | }; 249 | 250 | $.fn.tipsy.autoNS = function() { 251 | return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n'; 252 | }; 253 | 254 | $.fn.tipsy.autoWE = function() { 255 | return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w'; 256 | }; 257 | 258 | /** 259 | * yields a closure of the supplied parameters, producing a function that takes 260 | * no arguments and is suitable for use as an autogravity function like so: 261 | * 262 | * @param margin (int) - distance from the viewable region edge that an 263 | * element should be before setting its tooltip's gravity to be away 264 | * from that edge. 265 | * @param prefer (string, e.g. 'n', 'sw', 'w') - the direction to prefer 266 | * if there are no viewable region edges effecting the tooltip's 267 | * gravity. It will try to vary from this minimally, for example, 268 | * if 'sw' is preferred and an element is near the right viewable 269 | * region edge, but not the top edge, it will set the gravity for 270 | * that element's tooltip to be 'se', preserving the southern 271 | * component. 272 | */ 273 | $.fn.tipsy.autoBounds = function(margin, prefer) { 274 | return function() { 275 | var dir = {ns: prefer[0], ew: (prefer.length > 1 ? prefer[1] : false)}, 276 | boundTop = $(document).scrollTop() + margin, 277 | boundLeft = $(document).scrollLeft() + margin, 278 | $this = $(this); 279 | 280 | if ($this.offset().top < boundTop) dir.ns = 'n'; 281 | if ($this.offset().left < boundLeft) dir.ew = 'w'; 282 | if ($(window).width() + $(document).scrollLeft() - $this.offset().left < margin) dir.ew = 'e'; 283 | if ($(window).height() + $(document).scrollTop() - $this.offset().top < margin) dir.ns = 's'; 284 | 285 | return dir.ns + (dir.ew ? dir.ew : ''); 286 | }; 287 | }; 288 | })(jQuery); -------------------------------------------------------------------------------- /incident_overview/src/main/java/com/ibm/si/qradar/offenseviz/core/OffenseCollector.java: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | Copyright (c) 2015, IBM 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software distributed 8 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 9 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 10 | specific language governing permissions and limitations under the License. 11 | *********************************************************/ 12 | 13 | package com.ibm.si.qradar.offenseviz.core; 14 | 15 | import java.io.BufferedReader; 16 | import java.io.IOException; 17 | import java.io.InputStreamReader; 18 | import java.net.URI; 19 | import java.net.URISyntaxException; 20 | import java.security.KeyManagementException; 21 | import java.security.KeyStoreException; 22 | import java.security.NoSuchAlgorithmException; 23 | import java.security.cert.CertificateException; 24 | import java.security.cert.X509Certificate; 25 | import java.sql.Timestamp; 26 | import java.text.SimpleDateFormat; 27 | import java.util.ArrayList; 28 | import java.util.Date; 29 | import java.util.HashSet; 30 | import java.util.List; 31 | import java.util.Set; 32 | import java.util.TreeSet; 33 | 34 | import javax.persistence.EntityManager; 35 | 36 | import org.apache.commons.lang3.StringUtils; 37 | import org.apache.http.client.ClientProtocolException; 38 | import org.apache.http.client.methods.CloseableHttpResponse; 39 | import org.apache.http.client.methods.HttpGet; 40 | import org.apache.http.client.utils.URIBuilder; 41 | import org.apache.http.conn.ssl.AllowAllHostnameVerifier; 42 | import org.apache.http.conn.ssl.SSLContextBuilder; 43 | import org.apache.http.conn.ssl.TrustStrategy; 44 | import org.apache.http.impl.client.CloseableHttpClient; 45 | import org.apache.http.impl.client.HttpClients; 46 | import org.slf4j.Logger; 47 | import org.slf4j.LoggerFactory; 48 | 49 | import com.fasterxml.jackson.databind.ObjectMapper; 50 | import com.ibm.si.qradar.offenseviz.conf.QRadarConfig; 51 | import com.ibm.si.qradar.offenseviz.dao.DestinationDao; 52 | import com.ibm.si.qradar.offenseviz.dao.OffenseDao; 53 | import com.ibm.si.qradar.offenseviz.dao.SourceDao; 54 | import com.ibm.si.qradar.offenseviz.geoip.GeoInfo; 55 | import com.ibm.si.qradar.offenseviz.geoip.GeoipUtil; 56 | import com.ibm.si.qradar.offenseviz.jpa.Destination; 57 | import com.ibm.si.qradar.offenseviz.jpa.GeographicEndpoint; 58 | import com.ibm.si.qradar.offenseviz.jpa.Offense; 59 | import com.ibm.si.qradar.offenseviz.jpa.Source; 60 | import com.ibm.si.qradar.offenseviz.util.PersistenceUtil; 61 | 62 | public class OffenseCollector implements Runnable { 63 | 64 | private static final Logger logger = LoggerFactory.getLogger(OffenseCollector.class); 65 | private static final String OFFENSE_API_PATH = "/api/siem/offenses"; 66 | private static final String SOURCE_API_PATH = "/api/siem/source_addresses"; 67 | private static final String DEST_API_PATH = "/api/siem/local_destination_addresses"; 68 | 69 | private final Double defaultLatitude; 70 | private final Double defaultLongitude; 71 | private final String defaultCountry; 72 | private final String defaultCity; 73 | 74 | private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS"); 75 | 76 | private enum EntityType { SOURCE, DEST }; 77 | 78 | private OffenseDao offenseDao; 79 | private SourceDao sourceDao; 80 | private DestinationDao destDao; 81 | private GeoipUtil geoipUtil = null; 82 | 83 | ObjectMapper mapper; 84 | 85 | public OffenseCollector() { 86 | defaultLatitude = QRadarConfig.getInstance().getDefaultLatitude(); 87 | defaultLongitude = QRadarConfig.getInstance().getDefaultLongitude(); 88 | defaultCountry = QRadarConfig.getInstance().getDefaultCountry(); 89 | defaultCity = QRadarConfig.getInstance().getDefaultCity(); 90 | } 91 | 92 | @Override 93 | public void run() { 94 | EntityManager entityManager = null; 95 | 96 | try { 97 | geoipUtil = new GeoipUtil(); 98 | mapper = new ObjectMapper(); 99 | 100 | entityManager = PersistenceUtil.getEntityManagerFactory().createEntityManager(); 101 | offenseDao = new OffenseDao(entityManager); 102 | sourceDao = new SourceDao(entityManager); 103 | destDao = new DestinationDao(entityManager); 104 | 105 | getOffenses(); 106 | 107 | } catch (Exception e) { 108 | logger.error("Could not get offense list from QRadar", e); 109 | } finally { 110 | if(entityManager != null) { 111 | entityManager.close(); 112 | } 113 | if(geoipUtil != null) { 114 | geoipUtil.dispose(); 115 | } 116 | } 117 | } 118 | 119 | public void getOffenses() throws Exception { 120 | Date now = new Date(); 121 | Timestamp timestamp = new Timestamp(now.getTime()); 122 | 123 | logger.debug("Grabbing offenses at " + dateFormat.format(now)); 124 | 125 | String authToken = QRadarConfig.getInstance().getAuthorizationToken(); 126 | CloseableHttpClient httpclient = getHttpClient(); 127 | URI uri = getURI(OFFENSE_API_PATH); 128 | String json = executeGet(uri, httpclient, authToken); 129 | 130 | Date after = new Date(); 131 | logger.debug("Returned with offenses from QRadar at " + dateFormat.format(after)); 132 | logger.debug("It took " + (after.getTime() - now.getTime()) + " milliseconds to retrieve those offenses"); 133 | 134 | processOffenses(json, timestamp); 135 | } 136 | 137 | private String getSourcesOrDestsById(Set ids, EntityType type) throws Exception { 138 | String path; 139 | 140 | if (type == EntityType.SOURCE) { 141 | path = SOURCE_API_PATH; 142 | } else { 143 | path = DEST_API_PATH; 144 | } 145 | 146 | String authToken = QRadarConfig.getInstance().getAuthorizationToken(); 147 | CloseableHttpClient httpclient = getHttpClient(); 148 | String filter = buildIdFilter(ids); 149 | 150 | String qradarUrl = QRadarConfig.getInstance().getUrl(); 151 | URI uri = new URIBuilder().setScheme("https") 152 | .setHost(qradarUrl) 153 | .setPath(path) 154 | .setParameter("filter", filter) 155 | .build(); 156 | 157 | String json = executeGet(uri, httpclient, authToken); 158 | return json; 159 | 160 | } 161 | 162 | private List updateSources(Set ids) throws Exception{ 163 | 164 | Date before = new Date(); 165 | logger.debug("Getting sources at " + dateFormat.format(before)); 166 | 167 | String json = getSourcesOrDestsById(ids, EntityType.SOURCE); 168 | List sources = mapper.readValue(json, mapper.getTypeFactory() 169 | .constructCollectionType(List.class, Source.class)); 170 | 171 | List mergedSources = new ArrayList(); 172 | for(Source source : sources) { 173 | updateGeographicInfo(source); 174 | mergedSources.add(sourceDao.merge(source)); 175 | } 176 | 177 | Date after = new Date(); 178 | logger.debug("Sources retrieved at " + dateFormat.format(after)); 179 | logger.debug(String.valueOf(sources.size()) + " sources were processed in " + (after.getTime() - before.getTime()) + " milliseconds"); 180 | 181 | return mergedSources; 182 | } 183 | 184 | private List updateDestinations(Set ids) throws Exception { 185 | 186 | Date before = new Date(); 187 | logger.debug("Getting destinations at " + dateFormat.format(before)); 188 | 189 | String json = getSourcesOrDestsById(ids, EntityType.DEST); 190 | 191 | List dests = mapper.readValue(json, mapper.getTypeFactory() 192 | .constructCollectionType(List.class, Destination.class)); 193 | 194 | List mergedDests = new ArrayList(); 195 | for(Destination dest: dests) { 196 | updateGeographicInfo(dest); 197 | mergedDests.add(destDao.merge(dest)); 198 | } 199 | 200 | Date after = new Date(); 201 | logger.debug("Destinations retrieved at " + dateFormat.format(after)); 202 | logger.debug(String.valueOf(dests.size()) + " destinations were processed in " + (after.getTime() - before.getTime()) + " milliseconds"); 203 | 204 | return mergedDests; 205 | } 206 | 207 | private void updateGeographicInfo(GeographicEndpoint endpoint) throws Exception{ 208 | GeoInfo ginfo = geoipUtil.getGeoInfo(endpoint.getIp()); 209 | if(ginfo == null) { 210 | logger.debug("Could not lookup ip " + endpoint.getIp()); 211 | endpoint.setLatitude(defaultLatitude); 212 | endpoint.setLongitude(defaultLongitude); 213 | endpoint.setCountry(defaultCountry); 214 | endpoint.setCity(defaultCity); 215 | endpoint.setInternal(false); 216 | } else { 217 | logger.debug("Successfully looked up ip " + endpoint.getIp()); 218 | endpoint.setLatitude(ginfo.getLatitude()); 219 | endpoint.setLongitude(ginfo.getLongitude()); 220 | endpoint.setCountry(ginfo.getCountry()); 221 | endpoint.setCity(ginfo.getCity()); 222 | endpoint.setInternal(false); 223 | } 224 | } 225 | 226 | private String buildIdFilter(Set ids) { 227 | String filterPrefix = "id in %s"; 228 | String idString = StringUtils.join((Object) ids); 229 | String formattedIds = idString 230 | .replace("[", "(") 231 | .replace("]", ")") 232 | .trim(); 233 | String filter = String.format(filterPrefix, formattedIds); 234 | return filter; 235 | } 236 | 237 | private void processOffenses(String json, Timestamp timestamp) throws Exception { 238 | List offenses = mapper.readValue(json, mapper.getTypeFactory() 239 | .constructCollectionType(List.class, Offense.class)); 240 | 241 | logger.debug("Processing " + String.valueOf(offenses.size()) + " offenses at " + dateFormat.format(new Date())); 242 | 243 | for (Offense offense : offenses) { 244 | offense.setSeenAt(timestamp); 245 | offense.setSourceList(new ArrayList()); 246 | offense.setDestinationList(new ArrayList()); 247 | 248 | for (Integer id : offense.getSourceIds()) { 249 | Source source = new Source(id); 250 | offense.getSourceList().add(sourceDao.merge(source)); 251 | } 252 | 253 | for (Integer id : offense.getDestIds()) { 254 | Destination dest = new Destination(id); 255 | offense.getDestinationList().add(destDao.merge(dest)); 256 | } 257 | 258 | offenseDao.persist(offense); 259 | } 260 | 261 | updateSourcesAndDests(offenses); 262 | } 263 | 264 | private void updateSourcesAndDests(List offenses) { 265 | Set sourceIdSet = new HashSet(); 266 | Set destIdSet = new HashSet(); 267 | 268 | for (Offense offense : offenses) { 269 | sourceIdSet.addAll(offense.getSourceIds()); 270 | destIdSet.addAll(offense.getDestIds()); 271 | } 272 | 273 | try { 274 | updateSources(sourceIdSet); 275 | updateDestinations(destIdSet); 276 | } catch (Exception e) { 277 | logger.error("Failed to update offense sources and dests", e); 278 | } 279 | } 280 | 281 | private String executeGet(URI uri, CloseableHttpClient httpclient, String authToken) throws ClientProtocolException, IOException { 282 | HttpGet httpget = new HttpGet(uri); 283 | httpget.addHeader("SEC", authToken); 284 | CloseableHttpResponse response = httpclient.execute(httpget); 285 | BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8")); 286 | String json = reader.readLine(); 287 | response.close(); 288 | return json; 289 | } 290 | 291 | private CloseableHttpClient getHttpClient() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException { 292 | CloseableHttpClient httpclient = HttpClients.custom(). 293 | setHostnameVerifier(new AllowAllHostnameVerifier()). 294 | setSslcontext(new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() 295 | { 296 | public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException 297 | { 298 | return true; 299 | } 300 | }).build()).build(); 301 | 302 | return httpclient; 303 | } 304 | 305 | private URI getURI(String path) throws URISyntaxException { 306 | String qradarUrl = QRadarConfig.getInstance().getUrl(); 307 | URI uri = new URIBuilder().setScheme("https") 308 | .setHost(qradarUrl) 309 | .setPath(path) 310 | .addParameter("filter", "inactive=false and status=\"OPEN\"") 311 | .build(); 312 | 313 | return uri; 314 | } 315 | 316 | } 317 | -------------------------------------------------------------------------------- /incident_overview/src/main/webapp/js/d3.slider.js: -------------------------------------------------------------------------------- 1 | /* 2 | D3.js Slider 3 | Inspired by jQuery UI Slider 4 | Copyright (c) 2013, Bjorn Sandvik - http://blog.thematicmapping.org 5 | BSD license: http://opensource.org/licenses/BSD-3-Clause 6 | */ 7 | (function (root, factory) { 8 | if (typeof define === 'function' && define.amd) { 9 | // AMD. Register as an anonymous module. 10 | define(['d3'], factory); 11 | } else if (typeof exports === 'object') { 12 | if (process.browser) { 13 | // Browserify. Import css too using cssify. 14 | require('./d3.slider.css'); 15 | } 16 | // Node. Does not work with strict CommonJS, but 17 | // only CommonJS-like environments that support module.exports, 18 | // like Node. 19 | module.exports = factory(require('d3')); 20 | } else { 21 | // Browser globals (root is window) 22 | root.d3.slider = factory(root.d3); 23 | } 24 | }(this, function (d3) { 25 | return function module() { 26 | "use strict"; 27 | 28 | // Public variables width default settings 29 | var min = 0, 30 | max = 100, 31 | step = 0.01, 32 | animate = true, 33 | orientation = "horizontal", 34 | axis = false, 35 | margin = 50, 36 | value, 37 | active = 1, 38 | snap = false, 39 | scale; 40 | 41 | // Private variables 42 | var axisScale, 43 | dispatch = d3.dispatch("slide", "slideend"), 44 | formatPercent = d3.format(".2%"), 45 | tickFormat = d3.format(".0"), 46 | handle1, 47 | handle2 = null, 48 | sliderLength; 49 | 50 | function slider(selection) { 51 | selection.each(function() { 52 | 53 | // Create scale if not defined by user 54 | if (!scale) { 55 | scale = d3.scale.linear().domain([min, max]); 56 | } 57 | 58 | // Start value 59 | value = value || scale.domain()[0]; 60 | 61 | // DIV container 62 | var div = d3.select(this).classed("d3-slider d3-slider-" + orientation, true); 63 | 64 | var drag = d3.behavior.drag(); 65 | drag.on('dragend', function () { 66 | dispatch.slideend(d3.event, value); 67 | }) 68 | 69 | // Slider handle 70 | //if range slider, create two 71 | var divRange; 72 | 73 | if ( value.length == 2 ) { 74 | handle1 = div.append("a") 75 | .classed("d3-slider-handle", true) 76 | .attr("xlink:href", "#") 77 | .attr('id', "handle-one") 78 | .on("click", stopPropagation) 79 | .call(drag); 80 | handle2 = div.append("a") 81 | .classed("d3-slider-handle", true) 82 | .attr('id', "handle-two") 83 | .attr("xlink:href", "#") 84 | .on("click", stopPropagation) 85 | .call(drag); 86 | } else { 87 | handle1 = div.append("a") 88 | .classed("d3-slider-handle", true) 89 | .attr("xlink:href", "#") 90 | .attr('id', "handle-one") 91 | .on("click", stopPropagation) 92 | .call(drag); 93 | } 94 | 95 | // Horizontal slider 96 | if (orientation === "horizontal") { 97 | 98 | div.on("click", onClickHorizontal); 99 | 100 | if ( value.length == 2 ) { 101 | divRange = d3.select(this).append('div').classed("d3-slider-range", true); 102 | 103 | handle1.style("left", formatPercent(scale(value[ 0 ]))); 104 | divRange.style("left", formatPercent(scale(value[ 0 ]))); 105 | drag.on("drag", onDragHorizontal); 106 | 107 | var width = 100 - parseFloat(formatPercent(scale(value[ 1 ]))); 108 | handle2.style("left", formatPercent(scale(value[ 1 ]))); 109 | divRange.style("right", width+"%"); 110 | drag.on("drag", onDragHorizontal); 111 | 112 | } else { 113 | handle1.style("left", formatPercent(scale(value))); 114 | drag.on("drag", onDragHorizontal); 115 | } 116 | 117 | sliderLength = parseInt(div.style("width"), 10); 118 | 119 | } else { // Vertical 120 | 121 | div.on("click", onClickVertical); 122 | drag.on("drag", onDragVertical); 123 | if ( value.length == 2 ) { 124 | divRange = d3.select(this).append('div').classed("d3-slider-range-vertical", true); 125 | 126 | handle1.style("bottom", formatPercent(scale(value[ 0 ]))); 127 | divRange.style("bottom", formatPercent(scale(value[ 0 ]))); 128 | drag.on("drag", onDragVertical); 129 | 130 | var top = 100 - parseFloat(formatPercent(scale(value[ 1 ]))); 131 | handle2.style("bottom", formatPercent(scale(value[ 1 ]))); 132 | divRange.style("top", top+"%"); 133 | drag.on("drag", onDragVertical); 134 | 135 | } else { 136 | handle1.style("bottom", formatPercent(scale(value))); 137 | drag.on("drag", onDragVertical); 138 | } 139 | 140 | sliderLength = parseInt(div.style("height"), 10); 141 | 142 | } 143 | 144 | if (axis) { 145 | createAxis(div); 146 | } 147 | 148 | 149 | function createAxis(dom) { 150 | 151 | // Create axis if not defined by user 152 | if (typeof axis === "boolean") { 153 | 154 | axis = d3.svg.axis() 155 | .ticks(Math.round(sliderLength / 100)) 156 | .tickFormat(tickFormat) 157 | .orient((orientation === "horizontal") ? "bottom" : "right"); 158 | 159 | } 160 | 161 | // Copy slider scale to move from percentages to pixels 162 | axisScale = scale.copy().range([0, sliderLength]); 163 | axis.scale(axisScale); 164 | 165 | // Create SVG axis container 166 | var svg = dom.append("svg") 167 | .classed("d3-slider-axis d3-slider-axis-" + axis.orient(), true) 168 | .on("click", stopPropagation); 169 | 170 | var g = svg.append("g"); 171 | 172 | // Horizontal axis 173 | if (orientation === "horizontal") { 174 | 175 | svg.style("margin-left", -margin + "px"); 176 | 177 | svg.attr({ 178 | width: sliderLength + margin * 2, 179 | height: margin 180 | }); 181 | 182 | if (axis.orient() === "top") { 183 | svg.style("top", -margin + "px"); 184 | g.attr("transform", "translate(" + margin + "," + margin + ")"); 185 | } else { // bottom 186 | g.attr("transform", "translate(" + margin + ",0)"); 187 | } 188 | 189 | } else { // Vertical 190 | 191 | svg.style("top", -margin + "px"); 192 | 193 | svg.attr({ 194 | width: margin, 195 | height: sliderLength + margin * 2 196 | }); 197 | 198 | if (axis.orient() === "left") { 199 | svg.style("left", -margin + "px"); 200 | g.attr("transform", "translate(" + margin + "," + margin + ")"); 201 | } else { // right 202 | g.attr("transform", "translate(" + 0 + "," + margin + ")"); 203 | } 204 | 205 | } 206 | 207 | g.call(axis); 208 | 209 | } 210 | 211 | function onClickHorizontal() { 212 | if (!value.length) { 213 | var pos = Math.max(0, Math.min(sliderLength, d3.event.offsetX || d3.event.layerX)); 214 | moveHandle(stepValue(scale.invert(pos / sliderLength))); 215 | } 216 | } 217 | 218 | function onClickVertical() { 219 | if (!value.length) { 220 | var pos = sliderLength - Math.max(0, Math.min(sliderLength, d3.event.offsetY || d3.event.layerY)); 221 | moveHandle(stepValue(scale.invert(pos / sliderLength))); 222 | } 223 | } 224 | 225 | function onDragHorizontal() { 226 | if ( d3.event.sourceEvent.target.id === "handle-one") { 227 | active = 1; 228 | } else if ( d3.event.sourceEvent.target.id == "handle-two" ) { 229 | active = 2; 230 | } 231 | var pos = Math.max(0, Math.min(sliderLength, d3.event.x)); 232 | moveHandle(stepValue(scale.invert(pos / sliderLength))); 233 | } 234 | 235 | function onDragVertical() { 236 | if ( d3.event.sourceEvent.target.id === "handle-one") { 237 | active = 1; 238 | } else if ( d3.event.sourceEvent.target.id == "handle-two" ) { 239 | active = 2; 240 | } 241 | var pos = sliderLength - Math.max(0, Math.min(sliderLength, d3.event.y)) 242 | moveHandle(stepValue(scale.invert(pos / sliderLength))); 243 | } 244 | 245 | function stopPropagation() { 246 | d3.event.stopPropagation(); 247 | } 248 | 249 | }); 250 | 251 | } 252 | 253 | // Move slider handle on click/drag 254 | function moveHandle(newValue) { 255 | var currentValue = value.length ? value[active - 1]: value, 256 | oldPos = formatPercent(scale(stepValue(currentValue))), 257 | newPos = formatPercent(scale(stepValue(newValue))), 258 | position = (orientation === "horizontal") ? "left" : "bottom"; 259 | if (oldPos !== newPos) { 260 | 261 | if ( value.length === 2) { 262 | value[ active - 1 ] = newValue; 263 | if (d3.event) { 264 | dispatch.slide(d3.event, value ); 265 | }; 266 | } else { 267 | if (d3.event) { 268 | dispatch.slide(d3.event.sourceEvent || d3.event, value = newValue); 269 | }; 270 | } 271 | 272 | if ( value[ 0 ] >= value[ 1 ] ) return; 273 | if ( active === 1 ) { 274 | if (value.length === 2) { 275 | (position === "left") ? divRange.style("left", newPos) : divRange.style("bottom", newPos); 276 | } 277 | 278 | if (animate) { 279 | handle1.transition() 280 | .styleTween(position, function() { return d3.interpolate(oldPos, newPos); }) 281 | .duration((typeof animate === "number") ? animate : 250); 282 | } else { 283 | handle1.style(position, newPos); 284 | } 285 | } else { 286 | 287 | var width = 100 - parseFloat(newPos); 288 | var top = 100 - parseFloat(newPos); 289 | 290 | (position === "left") ? divRange.style("right", width + "%") : divRange.style("top", top + "%"); 291 | 292 | if (animate) { 293 | handle2.transition() 294 | .styleTween(position, function() { return d3.interpolate(oldPos, newPos); }) 295 | .duration((typeof animate === "number") ? animate : 250); 296 | } else { 297 | handle2.style(position, newPos); 298 | } 299 | } 300 | } 301 | } 302 | 303 | // Calculate nearest step value 304 | function stepValue(val) { 305 | 306 | if (val === scale.domain()[0] || val === scale.domain()[1]) { 307 | return val; 308 | } 309 | 310 | var alignValue = val; 311 | if (snap) { 312 | var val_i = scale(val); 313 | var dist = scale.ticks().map(function(d) {return val_i - scale(d);}); 314 | var i = -1, 315 | index = 0, 316 | r = scale.range()[1]; 317 | do { 318 | i++; 319 | if (Math.abs(dist[i]) < r) { 320 | r = Math.abs(dist[i]); 321 | index = i; 322 | }; 323 | } while (dist[i] > 0 && i < dist.length - 1); 324 | alignValue = scale.ticks()[index]; 325 | } else{ 326 | var valModStep = (val - scale.domain()[0]) % step; 327 | alignValue = val - valModStep; 328 | 329 | if (Math.abs(valModStep) * 2 >= step) { 330 | alignValue += (valModStep > 0) ? step : -step; 331 | } 332 | }; 333 | 334 | return alignValue; 335 | 336 | } 337 | 338 | // Getter/setter functions 339 | slider.min = function(_) { 340 | if (!arguments.length) return min; 341 | min = _; 342 | return slider; 343 | }; 344 | 345 | slider.max = function(_) { 346 | if (!arguments.length) return max; 347 | max = _; 348 | return slider; 349 | }; 350 | 351 | slider.step = function(_) { 352 | if (!arguments.length) return step; 353 | step = _; 354 | return slider; 355 | }; 356 | 357 | slider.animate = function(_) { 358 | if (!arguments.length) return animate; 359 | animate = _; 360 | return slider; 361 | }; 362 | 363 | slider.orientation = function(_) { 364 | if (!arguments.length) return orientation; 365 | orientation = _; 366 | return slider; 367 | }; 368 | 369 | slider.axis = function(_) { 370 | if (!arguments.length) return axis; 371 | axis = _; 372 | return slider; 373 | }; 374 | 375 | slider.margin = function(_) { 376 | if (!arguments.length) return margin; 377 | margin = _; 378 | return slider; 379 | }; 380 | 381 | slider.value = function(_) { 382 | if (!arguments.length) return value; 383 | if (value) { 384 | moveHandle(stepValue(_)); 385 | }; 386 | value = _; 387 | return slider; 388 | }; 389 | 390 | slider.snap = function(_) { 391 | if (!arguments.length) return snap; 392 | snap = _; 393 | return slider; 394 | }; 395 | 396 | slider.scale = function(_) { 397 | if (!arguments.length) return scale; 398 | scale = _; 399 | return slider; 400 | }; 401 | 402 | d3.rebind(slider, dispatch, "on"); 403 | 404 | return slider; 405 | 406 | } 407 | })); 408 | --------------------------------------------------------------------------------