You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by js...@apache.org on 2017/10/13 22:47:33 UTC

metron git commit: METRON-1223 Add support to add comments for alerts (iraghumitra via james-sirota) closes apache/metron#788

Repository: metron
Updated Branches:
  refs/heads/master bbfe29a97 -> 39bb85676


METRON-1223 Add support to add comments for alerts (iraghumitra via james-sirota) closes apache/metron#788


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/39bb8567
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/39bb8567
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/39bb8567

Branch: refs/heads/master
Commit: 39bb856762739ecd2103e3b67eed76d4fb655eaa
Parents: bbfe29a
Author: iraghumitra <ra...@gmail.com>
Authored: Fri Oct 13 15:47:17 2017 -0700
Committer: jsirota <js...@apache.org>
Committed: Fri Oct 13 15:47:17 2017 -0700

----------------------------------------------------------------------
 metron-interface/metron-alerts/README.md        |  5 +-
 .../e2e/alert-details/alert-details.po.ts       | 59 ++++++++++++-
 .../alert-details-status.e2e-spec.ts            | 34 +++++++-
 .../e2e/alerts-list/alerts-list.e2e-spec.ts     |  4 +-
 .../e2e/alerts-list/alerts-list.po.ts           |  4 +
 .../metron-alerts/e2e/login/login.po.ts         |  6 +-
 .../metron-alerts/e2e/utils/e2e_util.ts         | 16 +++-
 metron-interface/metron-alerts/package.json     |  2 +
 .../metron-alerts/src/_variables.scss           |  3 +
 .../app/alerts/alert-details/alert-comment.ts   | 29 +++++++
 .../alert-details/alert-details.component.html  | 91 ++++++++++++++------
 .../alert-details/alert-details.component.scss  | 77 ++++++++++++++++-
 .../alert-details/alert-details.component.ts    | 80 ++++++++++++++++-
 .../alert-details/alerts-details.module.ts      |  4 +-
 .../alert-details/alerts-details.routing.ts     |  2 +-
 .../alerts/alerts-list/alerts-list.component.ts |  7 +-
 .../table-view/table-view.component.html        |  4 +-
 .../src/app/login/login.component.html          |  4 +-
 .../src/app/login/login.component.scss          |  4 +
 .../src/app/login/login.component.ts            |  2 +-
 .../metron-alerts/src/app/model/alert-source.ts |  3 +
 .../metron-alerts/src/app/model/alert.ts        |  1 +
 .../src/app/model/patch-request.ts              |  6 +-
 .../metron-alerts/src/app/model/patch.ts        | 28 ++++++
 .../src/app/service/authentication.service.ts   |  4 +
 metron-interface/metron-alerts/src/styles.scss  | 10 ++-
 26 files changed, 433 insertions(+), 56 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/README.md
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/README.md b/metron-interface/metron-alerts/README.md
index b0433d0..3d43e3e 100644
--- a/metron-interface/metron-alerts/README.md
+++ b/metron-interface/metron-alerts/README.md
@@ -12,6 +12,9 @@ UI uses local storage to save all the data.  A middleware needs to be designed a
 ### Search for Alert GUIDs
 Alert GUIDs must be double-quoted when being searched on to ensure correctness of results, e.g. guid:"id1".
 
+### Search for Comments
+Users cannot search for the contents of the comment's in the Alerts-UI
+
 ## Prerequisites
 * The Metron REST application should be up and running and Elasticsearch should have some alerts populated by Metron topologies
 * The Management UI should be installed (which includes [Express](https://expressjs.com/))
@@ -58,7 +61,7 @@ Alert GUIDs must be double-quoted when being searched on to ensure correctness o
 
 ### From Ambari MPack
 
-The Alerts UI is included in the Metron Ambari MPack.  It can be accessed through the Quick Links in the Metron service.  
+The Alerts UI is included in the Metron Ambari MPack.  It can be accessed through the Quick Links in the Metron service.
 
 ## Configuration
 

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/e2e/alert-details/alert-details.po.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/alert-details/alert-details.po.ts b/metron-interface/metron-alerts/e2e/alert-details/alert-details.po.ts
index 79a0e1d..39aea0b 100644
--- a/metron-interface/metron-alerts/e2e/alert-details/alert-details.po.ts
+++ b/metron-interface/metron-alerts/e2e/alert-details/alert-details.po.ts
@@ -17,12 +17,25 @@
  */
 
 import {browser, element, by, protractor} from 'protractor';
+import {waitForElementInVisibility, waitForElementPresence, waitForElementVisibility} from '../utils/e2e_util';
 
 export class MetronAlertDetailsPage {
 
   navigateTo(alertId: string) {
     browser.waitForAngularEnabled(false);
-    return browser.get('/alerts-list(dialog:details/alerts_ui_e2e/'+ alertId +')');
+    browser.get('/alerts-list(dialog:details/alerts_ui_e2e/'+ alertId +'/alerts_ui_e2e_index)');
+    browser.sleep(2000);
+  }
+
+  addCommentAndSave(comment: string, index: number) {
+    let textAreaElement = element(by.css('app-alert-details textarea'));
+    let addCommentButtonElement = element(by.buttonText('ADD COMMENT'));
+    let latestCommentEle = element.all(by.css('.comment-container .comment')).get(index);
+
+    textAreaElement.clear()
+    .then(() => textAreaElement.sendKeys(comment))
+    .then(() => addCommentButtonElement.click())
+    .then(() => waitForElementPresence(latestCommentEle));
   }
 
   clickNew() {
@@ -45,6 +58,38 @@ export class MetronAlertDetailsPage {
     element.all(by.css('.metron-slider-pane-details table tbody tr')).get(2).all(by.css('td')).get(1).click();
   }
 
+  clickCommentsInSideNav() {
+    return element(by.css('app-alert-details .fa.fa-comment')).click();
+  }
+
+  clickNoForConfirmation() {
+    browser.sleep(1000);
+    let dialogElement = element(by.css('.metron-dialog .modal-header .close'));
+    let maskElement = element(by.css('.modal-backdrop.fade'));
+    waitForElementVisibility(dialogElement).then(() => element(by.css('.metron-dialog')).element(by.buttonText('Cancel')).click())
+    .then(() => waitForElementInVisibility(maskElement));
+  }
+
+  clickYesForConfirmation() {
+    browser.sleep(1000);
+    let dialogElement = element(by.css('.metron-dialog .modal-header .close'));
+    let maskElement = element(by.css('.modal-backdrop.fade'));
+    waitForElementVisibility(dialogElement).then(() => element(by.css('.metron-dialog')).element(by.buttonText('OK')).click())
+    .then(() => waitForElementInVisibility(maskElement));
+  }
+
+  closeDetailPane() {
+    element(by.css('app-alert-details .close-button')).click();
+    browser.sleep(2000);
+  }
+
+  deleteComment() {
+    let scrollToEle = element.all(by.css('.comment-container')).get(0);
+    let trashIcon = element.all(by.css('.fa.fa-trash-o')).get(0);
+    browser.actions().mouseMove(scrollToEle).perform().then(() => waitForElementVisibility(trashIcon))
+    .then(() => element.all(by.css('.fa.fa-trash-o')).get(0).click());
+  }
+
   getAlertStatus(previousText) {
     let alertStatusElement = element.all(by.css('.metron-slider-pane-details .form .row')).get(0).all(by.css('div')).get(1);
     return this.waitForTextChange(alertStatusElement, previousText).then(() => {
@@ -52,6 +97,18 @@ export class MetronAlertDetailsPage {
     });
   }
 
+  getCommentsText() {
+    return element.all(by.css('.comment-container .comment')).getText();
+  }
+
+  getCommentsUserNameAndTimeStamp() {
+    return element.all(by.css('.comment-container .username-timestamp')).getText();
+  }
+
+  getCommentIconCountInListView() {
+    return element.all(by.css('app-table-view .fa.fa-comments-o')).count();
+  }
+
   waitForTextChange(element, previousText) {
     let EC = protractor.ExpectedConditions;
     return browser.wait(EC.not(EC.textToBePresentInElement(element, previousText)));

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/e2e/alert-details/alert-status/alert-details-status.e2e-spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/alert-details/alert-status/alert-details-status.e2e-spec.ts b/metron-interface/metron-alerts/e2e/alert-details/alert-status/alert-details-status.e2e-spec.ts
index 4e7331c..58d4892 100644
--- a/metron-interface/metron-alerts/e2e/alert-details/alert-status/alert-details-status.e2e-spec.ts
+++ b/metron-interface/metron-alerts/e2e/alert-details/alert-status/alert-details-status.e2e-spec.ts
@@ -65,4 +65,36 @@ describe('metron-alerts alert status', function() {
     page.clickNew();
   });
 
-});
\ No newline at end of file
+  it('should add comments', () => {
+    let comment1 = 'This is a sample comment';
+    let comment2 = 'This is a sample comment again';
+    let userNameAndTimestamp = '- admin - a few seconds ago';
+
+    page.clickCommentsInSideNav();
+    page.addCommentAndSave(comment1, 0);
+
+    expect(page.getCommentsText()).toEqual([comment1]);
+    expect(page.getCommentsUserNameAndTimeStamp()).toEqual([userNameAndTimestamp]);
+
+    page.addCommentAndSave(comment2, 1);
+    expect(page.getCommentsText()).toEqual([comment2, comment1]);
+    expect(page.getCommentsUserNameAndTimeStamp()).toEqual([userNameAndTimestamp, userNameAndTimestamp]);
+
+    page.deleteComment();
+    page.clickNoForConfirmation();
+    expect(page.getCommentsText()).toEqual([comment2, comment1]);
+
+    page.deleteComment();
+    page.clickYesForConfirmation();
+    expect(page.getCommentsText()).toEqual([comment1]);
+
+    expect(page.getCommentIconCountInListView()).toEqual(1);
+
+    page.deleteComment();
+    page.clickYesForConfirmation();
+    expect(page.getCommentsText()).toEqual([]);
+
+    page.closeDetailPane();
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.e2e-spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.e2e-spec.ts b/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.e2e-spec.ts
index 8e92737..43290fe 100644
--- a/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.e2e-spec.ts
+++ b/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.e2e-spec.ts
@@ -19,13 +19,13 @@
 import { MetronAlertsPage } from './alerts-list.po';
 import { customMatchers } from  '../matchers/custom-matchers';
 import { LoginPage } from '../login/login.po';
-import { loadTestData, deleteTestData } from "../utils/e2e_util";
+import { loadTestData, deleteTestData } from '../utils/e2e_util';
 
 describe('metron-alerts App', function() {
   let page: MetronAlertsPage;
   let loginPage: LoginPage;
   let columnNames = [ 'Score', 'id', 'timestamp', 'source:type', 'ip_src_addr', 'enrichm...:country',
-                      'ip_dst_addr', 'host', 'alert_status', '', '' ];
+                      'ip_dst_addr', 'host', 'alert_status', '', '', '' ];
   let colNamesColumnConfig = [ 'score', 'id', 'timestamp', 'source:type', 'ip_src_addr', 'enrichments:geo:ip_dst_addr:country',
                                 'ip_dst_addr', 'host', 'alert_status' ];
 

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.po.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.po.ts b/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.po.ts
index 8e09f85..39aefa7 100644
--- a/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.po.ts
+++ b/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.po.ts
@@ -184,6 +184,10 @@ export class MetronAlertsPage {
     return element(by.css('.ace_line')).getText();
   }
 
+  isCommentIconPresentInTable() {
+    return element.all(by.css('app-table-view .fa.fa-comments-o')).count();
+  }
+
   getRecentSearchOptions() {
     browser.sleep(1000);
     let map = {};

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/e2e/login/login.po.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/login/login.po.ts b/metron-interface/metron-alerts/e2e/login/login.po.ts
index c9f8c23..2f0f81d 100644
--- a/metron-interface/metron-alerts/e2e/login/login.po.ts
+++ b/metron-interface/metron-alerts/e2e/login/login.po.ts
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 import { browser, element, by } from 'protractor';
-import {waitForElementVisibility} from '../utils/e2e_util';
+import {waitForElementVisibility, waitForURL} from '../utils/e2e_util';
 
 export class LoginPage {
     navigateToLogin() {
@@ -35,7 +35,7 @@ export class LoginPage {
         browser.waitForAngularEnabled(false);
         element.all(by.css('.alert .close')).click();
         element.all(by.css('.logout-link')).click();
-        browser.sleep(2000);
+        waitForURL('http://localhost:4200/login');
     }
 
     setUserNameAndPassword(userName: string, password: string) {
@@ -49,7 +49,7 @@ export class LoginPage {
 
     getErrorMessage() {
         browser.waitForAngularEnabled(false);
-        let errElement = element(by.css('div[style="color:#a94442"]'));
+        let errElement = element(by.css('.login-failed-msg'));
         return waitForElementVisibility(errElement).then(() => {
             return errElement.getText().then((message) => {
                 return message.replace(/\n/, '').replace(/LOG\ IN$/, '');

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/e2e/utils/e2e_util.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/utils/e2e_util.ts b/metron-interface/metron-alerts/e2e/utils/e2e_util.ts
index ce5609a..341e668 100644
--- a/metron-interface/metron-alerts/e2e/utils/e2e_util.ts
+++ b/metron-interface/metron-alerts/e2e/utils/e2e_util.ts
@@ -10,6 +10,16 @@ export function changeURL(url: string) {
     });
 }
 
+export function waitForURL(url: string) {
+  let EC = protractor.ExpectedConditions;
+  return browser.wait(EC.urlIs(url));
+}
+
+export function waitForText(element, text) {
+  let EC = protractor.ExpectedConditions;
+  return browser.wait(EC.textToBePresentInElement(element, text));
+}
+
 export function waitForElementInVisibility (_element ) {
     let EC = protractor.ExpectedConditions;
     return browser.wait(EC.invisibilityOf(_element));
@@ -32,8 +42,10 @@ export function waitForStalenessOf (_element ) {
 
 export function loadTestData() {
   deleteTestData();
-  fs.createReadStream('e2e/mock-data/alerts_ui_e2e_index.template').pipe(request.post('http://node1:9200/_template/alerts_ui_e2e_index'));
-  fs.createReadStream('e2e/mock-data/alerts_ui_e2e_index.data').pipe(request.post('http://node1:9200/alerts_ui_e2e_index/alerts_ui_e2e_doc/_bulk'));
+  fs.createReadStream('e2e/mock-data/alerts_ui_e2e_index.template')
+    .pipe(request.post('http://node1:9200/_template/alerts_ui_e2e_index'));
+  fs.createReadStream('e2e/mock-data/alerts_ui_e2e_index.data')
+    .pipe(request.post('http://node1:9200/alerts_ui_e2e_index/alerts_ui_e2e_doc/_bulk'));
 }
 
 export function deleteTestData() {

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/package.json
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/package.json b/metron-interface/metron-alerts/package.json
index 446c40d..6ff3c3c 100644
--- a/metron-interface/metron-alerts/package.json
+++ b/metron-interface/metron-alerts/package.json
@@ -25,6 +25,7 @@
     "bootstrap": "4.0.0-alpha.6",
     "core-js": "^2.4.1",
     "font-awesome": "^4.7.0",
+    "moment": "^2.18.1",
     "rxjs": "^5.1.0",
     "web-animations-js": "^2.2.2",
     "zone.js": "^0.8.4"
@@ -34,6 +35,7 @@
     "@angular/compiler-cli": "^4.0.0",
     "@types/ace": "0.0.32",
     "@types/jasmine": "2.5.38",
+    "@types/moment": "^2.13.0",
     "@types/node": "~6.0.60",
     "codelyzer": "~2.0.0",
     "compression": "1.6.2",

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/_variables.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/_variables.scss b/metron-interface/metron-alerts/src/_variables.scss
index 2705ff1..e4055ce 100644
--- a/metron-interface/metron-alerts/src/_variables.scss
+++ b/metron-interface/metron-alerts/src/_variables.scss
@@ -72,6 +72,9 @@ $eastern-blue: #1F91BE;
 $mantis: #80BF4D;
 $sky-blue: #75D2ED;
 $outer-space: #2E3A3F;
+$rolling-stone: #808285;
+$nile-blue: #18404E;
+$apple-blossom: #A94442;
 
 $eastern-blue-1: #1190C0;
 $matisse: #1E7490;

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-comment.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-comment.ts b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-comment.ts
new file mode 100644
index 0000000..a9cf802
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-comment.ts
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export class AlertComment {
+  comment = '';
+  username: string;
+  timestamp: number;
+
+  constructor(comment: string, username: string, timestamp: number) {
+    this.comment = comment;
+    this.username = username;
+    this.timestamp = timestamp;
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.html b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.html
index 8c82fab..1b5330e 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.html
+++ b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.html
@@ -12,35 +12,70 @@
   the specific language governing permissions and limitations under the License.
   -->
 <div class="metron-slider-pane-details load-right-to-left dialog1x">
-    <div class="container-fluid">
-        <div class="row mb-3">
-            <div class="col-md-10">
-                <div class="form-title" appAlertSeverity [severity]="alertSource['threat:triage:score']"> {{ alertSource['threat:triage:score'] }} {{ alertSource.guid }}</div>
+    <div class="container-fluid pl-0 h-100">
+        <div class="h-100 d-flex">
+            <div class="nav-container">
+                <ul class="nav flex-column">
+                    <li class="nav-item">
+                        <a class="nav-link" [ngClass]="{'active': activeTab === tabs.DETAILS}" (click)="activeTab=tabs.DETAILS">
+                            <i class="fa fa-info-circle" aria-hidden="true"></i>
+                        </a>
+                    </li>
+                    <li class="nav-item">
+                        <a class="nav-link" [ngClass]="{'active': activeTab === tabs.COMMENTS}" (click)="activeTab=tabs.COMMENTS">
+                            <i class="fa fa-comment" aria-hidden="true"></i>
+                        </a>
+                    </li>
+                </ul>
             </div>
-            <div class="col-md-2"><i class="fa fa-times pull-right close-button" aria-hidden="true" (click)="goBack()"></i></div>
-        </div>
-        <div class="row justify-content-center py-4 actions">
-            <table>
-                <tr>
-                    <td class="title"> Status</td>
-                    <td [ngClass]="{'primary': selectedAlertState === alertState.ESCALATE, 'secondary': selectedAlertState !== alertState.ESCALATE}" (click)="processEscalate()">ESCALATE</td>
-                    <td></td>
-                </tr>
-                <tr>
-                    <td [ngClass]="{'primary': selectedAlertState === alertState.NEW, 'secondary': selectedAlertState !== alertState.NEW}" (click)="processNew()">NEW</td>
-                    <td [ngClass]="{'primary': selectedAlertState === alertState.OPEN, 'secondary': selectedAlertState !== alertState.OPEN}" (click)="processOpen()">OPEN</td>
-                    <td [ngClass]="{'primary': selectedAlertState === alertState.DISMISS, 'secondary': selectedAlertState !== alertState.DISMISS}" (click)="processDismiss()">DISMISS</td>
-                </tr>
-                <tr>
-                    <td></td>
-                    <td [ngClass]="{'primary': selectedAlertState === alertState.RESOLVE, 'secondary': selectedAlertState !== alertState.RESOLVE}" (click)="processResolve()">RESOLVE</td>
-                    <td></td>
-                </tr>
-            </table>
-        </div>
-        <div class="ml-1 my-4 form">
-            <div *ngFor="let field of alertFields" class="row">
-              <div class="col-6 mb-1 key">{{ field }}</div>   <div class="col-6"> {{ alertSource[field] }} </div>
+            <div class="details-container">
+                <div class="container-fluid h-100">
+                    <div class="row title-container">
+                        <div class="col-md-10 px-0">
+                            <div class="form-title" appAlertSeverity [severity]="alertSource['threat:triage:score']"> {{ alertSource['threat:triage:score'] }} {{ alertSource.guid }}</div>
+                        </div>
+                        <div class="col-md-2 px-0">
+                            <i class="fa fa-times pull-right close-button" aria-hidden="true" (click)="goBack()"></i>
+                        </div>
+                    </div>
+                    <div class="row justify-content-center py-4 actions">
+                        <table>
+                            <tr>
+                                <td class="title"> Status</td>
+                                <td [ngClass]="{'primary': selectedAlertState === alertState.ESCALATE, 'secondary': selectedAlertState !== alertState.ESCALATE}" data-name="escalate" (click)="processEscalate()">ESCALATE</td>
+                                <td></td>
+                            </tr>
+                            <tr>
+                                <td [ngClass]="{'primary': selectedAlertState === alertState.NEW, 'secondary': selectedAlertState !== alertState.NEW}" data-name="new" (click)="processNew()">NEW</td>
+                                <td [ngClass]="{'primary': selectedAlertState === alertState.OPEN, 'secondary': selectedAlertState !== alertState.OPEN}" data-name="open" (click)="processOpen()">OPEN</td>
+                                <td [ngClass]="{'primary': selectedAlertState === alertState.DISMISS, 'secondary': selectedAlertState !== alertState.DISMISS}" data-name="dismiss" (click)="processDismiss()">DISMISS</td>
+                            </tr>
+                            <tr>
+                                <td></td>
+                                <td [ngClass]="{'primary': selectedAlertState === alertState.RESOLVE, 'secondary': selectedAlertState !== alertState.RESOLVE}" data-name="resolve" (click)="processResolve()">RESOLVE</td>
+                                <td></td>
+                            </tr>
+                        </table>
+                    </div>
+                    <div class="ml-1 my-4 form" *ngIf="activeTab === tabs.DETAILS">
+                        <div *ngFor="let field of alertFields" class="row">
+                            <div class="col-6 mb-1 key">{{ field }}</div>   <div class="col-6 value"> {{ alertSource[field] }} </div>
+                        </div>
+                    </div>
+                    <div *ngIf="activeTab === tabs.COMMENTS" class="my-4">
+                        <div> Comments <span *ngIf="alertCommentsWrapper.length > 0"> ({{alertCommentsWrapper.length}}) </span></div>
+                        <textarea class="form-control" [(ngModel)]="alertCommentStr"> </textarea>
+                        <button class="btn btn-mine_shaft_2" [disabled]="alertCommentStr.trim().length === 0" (click)="onAddComment()">ADD COMMENT</button>
+                        <ng-container *ngFor="let alertCommentWrapper of alertCommentsWrapper; let i = index">
+                            <hr>
+                            <div class="comment-container">
+                                <i class="fa fa-trash-o" aria-hidden="true" (click)="onDeleteComment(i)"></i>
+                                <div class="comment"> {{ alertCommentWrapper.alertComment.comment }} </div>
+                                <div class="font-italic username-timestamp"> - {{ alertCommentWrapper.alertComment.username }} - {{alertCommentWrapper.displayTime}}</div>
+                            </div>
+                        </ng-container>
+                    </div>
+                </div>
             </div>
         </div>
     </div>

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.scss b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.scss
index 8407bba..5899140 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.scss
+++ b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.scss
@@ -18,8 +18,8 @@
 @import "../../../slider";
 @import "../../../variables";
 
-.container-fluid {
-  padding-top: 15px;
+.title-container {
+  margin: 13px 0px;
 }
 
 .form-title {
@@ -70,3 +70,76 @@
     color: $dove-grey;
   }
 }
+
+.dialog1x {
+  width: 400px;
+}
+
+.nav-container {
+  min-height: 99vh;
+  width: 54px;
+  background: $mine-shaft-3;
+  border-right: 1px solid $dove-grey;
+
+  i {
+    cursor: pointer;
+    font-size: 19px;
+    color: $dusty-grey;
+  }
+
+  .active i {
+    color: #FFFFFF;
+  }
+
+  .nav-link {
+    padding: 15px 1em;
+
+    &.active {
+      border-left: 4px solid #31ABDF;
+      background: $mine-shaft-2;
+    }
+  }
+}
+
+.details-container {
+  width: 340px;
+}
+
+textarea {
+  margin-top: 5px;
+  height: 100px;
+}
+
+.btn-mine_shaft_2 {
+  margin-top: 10px;
+  margin-bottom: 15px;
+}
+
+.username-timestamp {
+  color: $rolling-stone;
+}
+
+.comment-container {
+  font-size: 14px;
+  padding: 10px 0px;
+
+  i {
+    right: 25px;
+    display: none;
+    cursor: pointer;
+    font-size: 18px;
+    position: absolute;
+  }
+
+  .comment {
+    padding-right: 25px;
+  }
+
+  &:hover {
+    background: $nile-blue;
+  }
+}
+
+.comment-container:hover i {
+  display: block;
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.ts b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.ts
index 2efb2ce..3d5b70e 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.ts
@@ -17,16 +17,37 @@
  */
 import { Component, OnInit } from '@angular/core';
 import {Router, ActivatedRoute} from '@angular/router';
+import * as moment from 'moment/moment';
+
 import {SearchService} from '../../service/search.service';
 import {UpdateService} from '../../service/update.service';
 import {Alert} from '../../model/alert';
 import {AlertsService} from '../../service/alerts.service';
 import {AlertSource} from '../../model/alert-source';
+import {PatchRequest} from '../../model/patch-request';
+import {Patch} from '../../model/patch';
+import {AlertComment} from './alert-comment';
+import {AuthenticationService} from '../../service/authentication.service';
+import {MetronDialogBox} from '../../shared/metron-dialog-box';
 
 export enum AlertState {
   NEW, OPEN, ESCALATE, DISMISS, RESOLVE
 }
 
+export enum Tabs {
+  DETAILS, COMMENTS
+}
+
+class AlertCommentWrapper {
+  alertComment: AlertComment;
+  displayTime: string;
+
+  constructor(alertComment: AlertComment, displayTime: string) {
+    this.alertComment = alertComment;
+    this.displayTime = displayTime;
+  }
+}
+
 @Component({
   selector: 'app-alert-details',
   templateUrl: './alert-details.component.html',
@@ -36,16 +57,25 @@ export class AlertDetailsComponent implements OnInit {
 
   alertId = '';
   alertSourceType = '';
+  alertIndex = '';
   alertState = AlertState;
+  tabs = Tabs;
+  activeTab = Tabs.DETAILS;
   selectedAlertState: AlertState = AlertState.NEW;
   alertSource: AlertSource = new AlertSource();
   alertFields: string[] = [];
+  alertCommentStr = '';
+  alertCommentsWrapper: AlertCommentWrapper[] = [];
 
   constructor(private router: Router,
               private activatedRoute: ActivatedRoute,
               private searchService: SearchService,
               private updateService: UpdateService,
-              private alertsService: AlertsService) { }
+              private alertsService: AlertsService,
+              private authenticationService: AuthenticationService,
+              private metronDialogBox: MetronDialogBox) {
+
+  }
 
   goBack() {
     this.router.navigateByUrl('/alerts-list');
@@ -53,13 +83,22 @@ export class AlertDetailsComponent implements OnInit {
   }
 
   getData() {
+    this.alertCommentStr = '';
     this.searchService.getAlert(this.alertSourceType, this.alertId).subscribe(alert => {
       this.alertSource = alert;
-      this.alertFields = Object.keys(alert).filter(field => !field.includes(':ts') && field !== 'original_string').sort();
+      this.alertFields = Object.keys(alert).filter(field => !field.includes(':ts') && field !== 'original_string' && field !== 'comments')
+                          .sort();
       this.selectedAlertState = this.getAlertState(alert['alert_status']);
+      this.setComments(alert);
     });
   }
 
+  setComments(alert) {
+    let alertComments = alert['comments'] ? alert['comments'] : [];
+    this.alertCommentsWrapper = alertComments.map(alertComment =>
+        new AlertCommentWrapper(alertComment, moment(new Date(alertComment.timestamp)).fromNow()));
+  }
+
   getAlertState(alertStatus) {
     if (alertStatus === 'OPEN') {
       return AlertState.OPEN;
@@ -78,9 +117,10 @@ export class AlertDetailsComponent implements OnInit {
     this.activatedRoute.params.subscribe(params => {
       this.alertId = params['guid'];
       this.alertSourceType = params['sourceType'];
+      this.alertIndex = params['index'];
       this.getData();
     });
-  }
+  };
 
   processOpen() {
     let tAlert = new Alert();
@@ -133,6 +173,40 @@ export class AlertDetailsComponent implements OnInit {
     });
   }
 
+  onAddComment() {
+    let alertComment = new AlertComment(this.alertCommentStr, this.authenticationService.getCurrentUserName(), new Date().getTime());
+    let tAlertComments = this.alertCommentsWrapper.map(alertsWrapper => alertsWrapper.alertComment);
+    tAlertComments.unshift(alertComment);
+    this.patchAlert(new Patch('add', '/comments', tAlertComments));
+  }
+
+  patchAlert(patch: Patch) {
+    let patchRequest = new PatchRequest();
+    patchRequest.guid = this.alertSource.guid;
+    patchRequest.index = this.alertIndex;
+    patchRequest.patch = [patch];
+    patchRequest.sensorType = this.alertSourceType;
+
+    this.updateService.patch(patchRequest).subscribe(() => {
+      this.getData();
+    });
+  }
+
+  onDeleteComment(index: number) {
+    let commentText =  'Do you wish to delete the comment ';
+    if (this.alertCommentsWrapper[index].alertComment.comment.length > 25 ) {
+      commentText += ' \'' + this.alertCommentsWrapper[index].alertComment.comment.substr(0, 25) + '...\'';
+    } else {
+      commentText += ' \'' + this.alertCommentsWrapper[index].alertComment.comment + '\'';
+    }
+
+    this.metronDialogBox.showConfirmationMessage(commentText).subscribe(response => {
+      if (response) {
+        this.alertCommentsWrapper.splice(index, 1);
+        this.patchAlert(new Patch('add', '/comments', this.alertCommentsWrapper.map(alertsWrapper => alertsWrapper.alertComment)));
+      }
+    });
+  }
 }
 
 

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/app/alerts/alert-details/alerts-details.module.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/alerts/alert-details/alerts-details.module.ts b/metron-interface/metron-alerts/src/app/alerts/alert-details/alerts-details.module.ts
index 9b4927e..d1e36a6 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alert-details/alerts-details.module.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/alert-details/alerts-details.module.ts
@@ -20,11 +20,11 @@ import {routing} from './alerts-details.routing';
 import {SharedModule} from '../../shared/shared.module';
 import {AlertDetailsComponent} from './alert-details.component';
 import {AlertsService} from '../../service/alerts.service';
-import {UpdateService} from '../../service/update.service';
+import {AuthenticationService} from '../../service/authentication.service';
 
 @NgModule ({
     imports: [ routing,  SharedModule],
     declarations: [ AlertDetailsComponent ],
-    providers: [ AlertsService, UpdateService ],
+    providers: [ AuthenticationService, AlertsService ]
 })
 export class AlertDetailsModule { }

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/app/alerts/alert-details/alerts-details.routing.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/alerts/alert-details/alerts-details.routing.ts b/metron-interface/metron-alerts/src/app/alerts/alert-details/alerts-details.routing.ts
index bc4dd5b..0cb9c9c 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alert-details/alerts-details.routing.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/alert-details/alerts-details.routing.ts
@@ -20,5 +20,5 @@ import { RouterModule } from '@angular/router';
 import {AlertDetailsComponent} from './alert-details.component';
 
 export const routing: ModuleWithProviders = RouterModule.forChild([
-    { path: 'details/:sourceType/:guid', component: AlertDetailsComponent, outlet: 'dialog'}
+    { path: 'details/:sourceType/:guid/:index', component: AlertDetailsComponent, outlet: 'dialog'}
 ]);

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.ts b/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.ts
index 189c9ba..46b7796 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.ts
@@ -314,10 +314,11 @@ export class AlertsListComponent implements OnInit, OnDestroy {
   }
 
   showDetails(alert: Alert) {
+    let url = '/alerts-list(dialog:details/' + alert.source['source:type'] + '/' + alert.source.guid + '/' + alert.index + ')';
     this.selectedAlerts = [];
     this.selectedAlerts = [alert];
     this.saveRefreshState();
-    this.router.navigateByUrl('/alerts-list(dialog:details/' + alert.source['source:type'] + '/' + alert.source.guid + ')');
+    this.router.navigateByUrl(url);
   }
 
   saveRefreshState() {
@@ -367,7 +368,7 @@ export class AlertsListComponent implements OnInit, OnDestroy {
 
   updateAlert(patchRequest: PatchRequest) {
     this.searchService.getAlert(patchRequest.sensorType, patchRequest.guid).subscribe(alertSource => {
-      this.alerts.filter(alert => alert.source.guid == patchRequest.guid)
+      this.alerts.filter(alert => alert.source.guid === patchRequest.guid)
       .map(alert => alert.source = alertSource);
     });
   }
@@ -375,7 +376,7 @@ export class AlertsListComponent implements OnInit, OnDestroy {
   updateSelectedAlertStatus(status: string) {
     for (let selectedAlert of this.selectedAlerts) {
       selectedAlert.status = status;
-      this.alerts.filter(alert => alert.source.guid == selectedAlert.source.guid)
+      this.alerts.filter(alert => alert.source.guid === selectedAlert.source.guid)
       .map(alert => alert.source['alert_status'] = status);
     }
     this.selectedAlerts = [];

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.html b/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.html
index ae788fc..561b299 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.html
+++ b/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.html
@@ -17,6 +17,7 @@
     <tr>
       <th style="width:55px"> <metron-config-sorter [type]="'number'" [sortBy]="threatScoreFieldName"> Score </metron-config-sorter> </th>
       <th *ngFor="let column of alertsColumnsToDisplay" [id]="column.name"> <metron-config-sorter [type]="column.type" [sortBy]="column.name" title="{{column.name}}"> {{ column.name | columnNameTranslate | centerEllipses:15 }}</metron-config-sorter> </th>
+      <th></th>
       <th style="width:25px"><i class="fa fa-cog configure-table-icon" (click)="showConfigureTable()"></i></th>
       <th style="width:25px"><input id="select-deselect-all" class="fontawesome-checkbox" type="checkbox" (click)="selectAllRows($event)"><label for="select-deselect-all"></label></th>
     </tr>
@@ -29,7 +30,8 @@
       <td *ngFor="let column of alertsColumnsToDisplay" #cell>
         <a (click)="addFilter(column.name, getValue(alert, column, false))" title="{{getValue(alert, column, true)}}" style="color:#689AA9">{{ getValue(alert, column, true) | centerEllipses:20:cell }}</a>
       </td>
-      <td></td>
+      <td> <i class="fa fa-comments-o" aria-hidden="true" *ngIf="alert.source.comments && alert.source.comments.length > 0"></i> </td>
+      <td> </td>
       <td><input id="{{ alert.id }}" class="fontawesome-checkbox" type="checkbox" name="{{alert.id}}" (click)="selectRow($event, alert)" [checked]="selectedAlerts.indexOf(alert) != -1"><label attr.for="{{ alert.id }}"></label></td>
     </tr>
     </tbody>

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/app/login/login.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/login/login.component.html b/metron-interface/metron-alerts/src/app/login/login.component.html
index 5ae746f..edf0c8d 100644
--- a/metron-interface/metron-alerts/src/app/login/login.component.html
+++ b/metron-interface/metron-alerts/src/app/login/login.component.html
@@ -20,8 +20,8 @@
     <input class="form-control" name="user" [(ngModel)]="user" required autofocus>
     <label class="label"> PASSWORD </label>
     <input type="password" name="password" class="form-control" [(ngModel)]="password" required>
-    <div class="my-4" style="color:#a94442">
-      {{loginFailure}}
+    <div class="my-4">
+      <div class="login-failed-msg" *ngIf="loginFailure.length > 0">{{loginFailure}}</div>
       <button class="btn btn-primary pull-right" type="submit" (click)="login()">LOG IN</button>
     </div>
   </form>

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/app/login/login.component.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/login/login.component.scss b/metron-interface/metron-alerts/src/app/login/login.component.scss
index b8d5416..d11c433 100644
--- a/metron-interface/metron-alerts/src/app/login/login.component.scss
+++ b/metron-interface/metron-alerts/src/app/login/login.component.scss
@@ -53,3 +53,7 @@
   width: 390px;
   height: 120px;
 }
+
+.login-failed-msg {
+  color: $apple-blossom;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/app/login/login.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/login/login.component.ts b/metron-interface/metron-alerts/src/app/login/login.component.ts
index 1d6c24f..9b6d66c 100644
--- a/metron-interface/metron-alerts/src/app/login/login.component.ts
+++ b/metron-interface/metron-alerts/src/app/login/login.component.ts
@@ -28,7 +28,7 @@ export class LoginComponent {
 
   user: string;
   password: string;
-  loginFailure: string = '';
+  loginFailure = '';
 
   constructor(private authenticationService: AuthenticationService, private activatedRoute: ActivatedRoute) {
     this.activatedRoute.queryParams.subscribe(params => {

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/app/model/alert-source.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/model/alert-source.ts b/metron-interface/metron-alerts/src/app/model/alert-source.ts
index cbf83d3..4e3a655 100644
--- a/metron-interface/metron-alerts/src/app/model/alert-source.ts
+++ b/metron-interface/metron-alerts/src/app/model/alert-source.ts
@@ -15,6 +15,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+import {AlertComment} from '../alerts/alert-details/alert-comment';
+
 export class AlertSource {
   msg: string;
   sig_rev: number;
@@ -42,6 +44,7 @@ export class AlertSource {
   guid: string;
   sig_id: number;
   sig_generator: number;
+  comments: AlertComment[] = [];
   'threat:triage:score': number;
   'threatinteljoinbolt:joiner:ts': number;
   'enrichmentsplitterbolt:splitter:begin:ts': number;

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/app/model/alert.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/model/alert.ts b/metron-interface/metron-alerts/src/app/model/alert.ts
index 59843a0..5a4e73b 100644
--- a/metron-interface/metron-alerts/src/app/model/alert.ts
+++ b/metron-interface/metron-alerts/src/app/model/alert.ts
@@ -21,4 +21,5 @@ export class Alert {
   score: number;
   source: AlertSource;
   status: string;
+  index: string;
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/app/model/patch-request.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/model/patch-request.ts b/metron-interface/metron-alerts/src/app/model/patch-request.ts
index 8e984ab..79db2d7 100644
--- a/metron-interface/metron-alerts/src/app/model/patch-request.ts
+++ b/metron-interface/metron-alerts/src/app/model/patch-request.ts
@@ -15,10 +15,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+import {Patch} from './patch';
+
 export class PatchRequest {
   guid: string;
   sensorType: string;
   index: string;
-  patch: {};
+  patch: Patch[] = [];
   source: {};
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/app/model/patch.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/model/patch.ts b/metron-interface/metron-alerts/src/app/model/patch.ts
new file mode 100644
index 0000000..4ce29f1
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/model/patch.ts
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export class Patch {
+  op: 'add' | 'remove';
+  path: string;
+  value: any;
+
+  constructor(op, path: string, value: any) {
+    this.op = op;
+    this.path = path;
+    this.value = value;
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/app/service/authentication.service.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/service/authentication.service.ts b/metron-interface/metron-alerts/src/app/service/authentication.service.ts
index f4924a5..e66725d 100644
--- a/metron-interface/metron-alerts/src/app/service/authentication.service.ts
+++ b/metron-interface/metron-alerts/src/app/service/authentication.service.ts
@@ -81,6 +81,10 @@ export class AuthenticationService {
     return this.http.get(this.loginUrl, options);
   }
 
+  public getCurrentUserName(): string {
+    return this.currentUser;
+  }
+
   public isAuthenticationChecked(): boolean {
     return this.currentUser !== AuthenticationService.USER_NOT_VERIFIED;
   }

http://git-wip-us.apache.org/repos/asf/metron/blob/39bb8567/metron-interface/metron-alerts/src/styles.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/styles.scss b/metron-interface/metron-alerts/src/styles.scss
index 12ac9f7..e3a0622 100644
--- a/metron-interface/metron-alerts/src/styles.scss
+++ b/metron-interface/metron-alerts/src/styles.scss
@@ -235,10 +235,18 @@ form
   color: $piction-blue;
   font-size: 14px;
   min-width: 85px;
-  width: 85px;
   cursor: pointer;
   &:focus
   {
     outline: none;
   }
+}
+
+hr {
+  display: block;
+  height: 1px;
+  border: 0;
+  border-top: 1px solid $tundora-1;
+  margin: 0.3rem 0;
+  padding: 0;
 }
\ No newline at end of file