Name
global.ISCNotificationTemplateUtility
Description
No description available
Script
var ISCNotificationTemplateUtility = Class.create();
ISCNotificationTemplateUtility.prototype = {
constants: new ISCConstants(),
initialize: function() {
},
/**
* This function will be used to gather and return the weekly digest information.
*/
weeklyDigest: function() {
// Reference to self
var that = this;
// Collect information for weekly digest
var lastWeekDateTime = gs.daysAgo(7);
// Collect info from security dashboard events
var failedLogins = _getDashboardEvent(lastWeekDateTime, this.constants.dashEvents.LOGIN_FAILED);
var externalLogins = _getDashboardEvent(lastWeekDateTime, this.constants.dashEvents.LOGIN_EXTERNAL);
var securityElevations = _getDashboardEvent(lastWeekDateTime, this.constants.dashEvents.SECURITY_ELEVATION);
var sncLogins = _getDashboardEvent(lastWeekDateTime, this.constants.dashEvents.LOGIN_SNC);
var adminLogins = _getDashboardEvent(lastWeekDateTime, this.constants.dashEvents.LOGIN_ADMIN);
var impersonations = _getDashboardEvent(lastWeekDateTime, this.constants.dashEvents.IMPERSONATION);
var exports = _getDashboardEvent(lastWeekDateTime, this.constants.dashEvents.EXPORT);
// Everything else
var complianceScore = _getComplianceScore();
var quarantinedFiles = _getQuarantinedFiles(lastWeekDateTime);
var virusTypes = _getVirusTypes(lastWeekDateTime);
var adminUsersAdded = _getAdminUsersAdded(lastWeekDateTime);
var spamEmails = _getSpamEmails(lastWeekDateTime);
var inactiveHpUsers = _getHpInactiveUsers();
return {
failedLogins: failedLogins.toString(),
externalLogins: externalLogins.toString(),
securityElevations: securityElevations.toString(),
sncLogins: sncLogins.toString(),
adminLogins: adminLogins.toString(),
complianceScore: complianceScore.toString(),
quarantinedFiles: quarantinedFiles.toString(),
virusTypes: virusTypes.toString(),
adminUsersAdded: adminUsersAdded.toString(),
impersonations: impersonations.toString(),
spamEmails: spamEmails.toString(),
inactiveHpUsers: inactiveHpUsers.toString(),
exports: exports.toString(),
};
function _getDashboardEvent(past, event) {
// Get the appropriate event table and return the event count
var eventTable = that.constants.dashEventToTable[event];
var iscEventGr = new GlideRecord(eventTable);
iscEventGr.addQuery('sys_created_on', '>=', past);
// Filter down for login events
if (event.includes('login')) {
iscEventGr.addQuery('login_type', event);
}
iscEventGr.query();
return iscEventGr.getRowCount();
}
// Get compliance score
// Calculation: .75 * (# of High Configured / # of High Available) +
// .15 (# of Mid Configured / # of Mid Available) + .1 (# of Low Configured / # of Low Available)
function _getComplianceScore() {
var REQUIREMENT = {
HIGH: 1,
MEDIUM: 2,
LOW: 3
};
var numHighConfig = _getNumConfig(REQUIREMENT.HIGH);
var numHigh = _getNum(REQUIREMENT.HIGH);
var numMedConfig = _getNumConfig(REQUIREMENT.MEDIUM);
var numMed = _getNum(REQUIREMENT.MEDIUM);
var numLowConfig = _getNumConfig(REQUIREMENT.LOW);
var numLow = _getNum(REQUIREMENT.LOW);
return _calculateComplianceScore(numHighConfig, numHigh, numMedConfig, numMed, numLowConfig, numLow);
function _getNumConfig(requirement) {
var ga = new GlideAggregate(that.constants.tables.ISC_SECURITY_CONFIGURATIONS);
ga.addQuery('harc_compliance_state', 'Pass');
ga.addQuery('harc_requirement', requirement);
ga.addAggregate('COUNT');
ga.query();
if (ga.next()) {
return ga.getAggregate('COUNT');
} else {
return 0;
}
}
function _getNum(requirement) {
ga = new GlideAggregate(that.constants.tables.ISC_SECURITY_CONFIGURATIONS);
ga.addQuery('harc_requirement', requirement);
ga.addAggregate('COUNT');
ga.query();
if (ga.next()) {
return ga.getAggregate('COUNT');
} else {
return 0;
}
}
function _calculateComplianceScore(numHighConfig, numHigh, numMedConfig, numMed, numLowConfig, numLow) {
// If there are no compliance items under the requirement then this formula will break
if (numHigh === 0 || numMed === 0 || numLow === 0) {
return -1;
} else {
return Math.floor(100 * (.005 + .75 * (numHighConfig / numHigh) +
.15 * (numMedConfig / numMed) +
.1 * (numLowConfig / numLow)));
}
}
}
// Get quarantined files since past
function _getQuarantinedFiles(past) {
var quarantinedFilesGa = new GlideAggregate("attachment_scan_history");
quarantinedFilesGa.addQuery('sys_created_on', '>=', past);
quarantinedFilesGa.addQuery('status', 'infected');
quarantinedFilesGa.addAggregate('COUNT');
quarantinedFilesGa.query();
if (quarantinedFilesGa.next()) {
return quarantinedFilesGa.getAggregate('COUNT');
} else {
return 0;
}
}
// Get all viruses detected since past
function _getVirusTypes(past) {
var quarantinedFilesGa = new GlideAggregate("attachment_scan_history");
quarantinedFilesGa.addQuery('sys_created_on', '>=', past);
quarantinedFilesGa.addAggregate('COUNT(DISTINCT', 'virus');
quarantinedFilesGa.query();
if (quarantinedFilesGa.next()) {
return quarantinedFilesGa.getAggregate('COUNT(DISTINCT', 'virus');
} else {
return 0;
}
}
// Get admin users since past
function _getAdminUsersAdded(past) {
var sysUserHasRoleGa = new GlideAggregate(that.constants.tables.USER_HAS_ROLE);
sysUserHasRoleGa.addQuery('role', that.constants.ADMIN_ROLE_SYS_ID);
sysUserHasRoleGa.addQuery('sys_created_on', '>=', past);
sysUserHasRoleGa.addAggregate('COUNT');
sysUserHasRoleGa.query();
if (sysUserHasRoleGa.next()) {
return sysUserHasRoleGa.getAggregate('COUNT');
} else {
return 0;
}
}
// Get spam emails since past
function _getSpamEmails(past) {
var sysEmailGa = new GlideAggregate(that.constants.tables.EMAIL);
sysEmailGa.addQuery('mailbox.name', 'Junk');
sysEmailGa.addQuery('sys_created_on', '>=', past);
sysEmailGa.addAggregate('COUNT');
sysEmailGa.query();
if (sysEmailGa.next()) {
return sysEmailGa.getAggregate('COUNT');
} else {
return 0;
}
}
// Get number of inactive High Privilege Users (admin, security_admin, impersonator, oauth_admin)
function _getHpInactiveUsers() {
var sysUserGr = new GlideAggregate(that.constants.tables.SYS_USER);
var lastLoginQuery = 'last_login_time<javascript:gs.beginningOfLast30Days()^roles=admin^ORroles=security_admin^ORroles=oauth_admin^ORroles=impersonator';
sysUserGr.addEncodedQuery(lastLoginQuery);
sysUserGr.addAggregate('COUNT');
sysUserGr.query();
return sysUserGr.next() ? sysUserGr.getAggregate('COUNT') : 0;
}
},
/**
* This function will return the styling for the ISC notification emails
*/
getStyling: function() {
// Take bootstrap CSS to match rest of instanceĆ
var styles = "<style>" +
"* { box-sizing: border-box }" +
"table { border-collapse: collapse; padding: 0; }" +
"th { text-align: inherit; }" +
".table { width: 100%; max-width: 100%; margin-bottom: 1rem; background-color: transparent; font-size: 14px; color: #293e40; }" +
".table thead th { vertical-align: bottom; border-bottom: 2px solid #dee2e6 }" +
".table td { padding: .75rem; vertical-align: top; border-top: 1px solid #dee2e6; padding: 0px; }" +
".email-content .title { font-weight: 600; }" +
".email-content .body { font-size: 14px }" +
"a { text-decoration: none; color: #1f8476; } " +
"h4 { margin-top: 10px; margin-bottom: 10px; }" +
"p { margin: 0 0 10px; font-size: 14px; } " +
".progress { overflow: hidden; height: 20px; margin-bottom: 20px; border-radius: 4px; " +
"-webkit-box-shadow: inset 0 1px 2px rgba (0, 0, 0, .1); box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); }" +
".progress-bar { float: left; width: 0%; height: 100%; font-size: 12px; line-height: 20px; color: #000; text-align: center; " +
"background-color: #1f8476; -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); " +
"-webkit-transition: width: .6 ease; -0-transition: width .6 ease; transition: width .6s ease; }" +
".progress-bar-success { background-color: #4CA965; }" +
".progress-bar-warning { background-color: #F9C642; }" +
".progress-bar-danger { background-color: #E83C36; }" +
".flex-column { display: -ms-flexbox; display: -moz-flex; display: -webkit-box; display: -webkit-flex; display: flex; " +
"-ms-flex-flow: column nowrap; -moz-flex-flow: column nowrap; -webkit-flex-flow: column nowrap; flex-flow: column nowrap }" +
".flex-row { display: -ms-flexbox; display: -moz-flex; display: -webkit-box; display: -webkit-flex; display: flex; " +
"-ms-flex-flow: row nowrap; -moz-flex-flow: row nowrap; -webkit-flex-flow: row nowrap; flex-flow: row nowrap }" +
".card { height: 90px; background-color: #F1F1F1; margin: 10px; }" +
".card .card-image { width: 90px; height: 90px; background-color: #293e40; text-align: center; align-items: center; justify-content: center; } " +
".card .card-image .number { align-items: center; justify-content: center; color: white; font-size: 30px; } " +
".card .card-content { margin-left: 1rem; padding: 10px; font-size: 14px; }" +
".card .card-content h5 { font-weight: 600; } " +
".card .card-content h5, p { margin: 0; line-height: 24px; font-size: 14px; } " +
".card .link { font-size: 12px; } " +
".table.wrap-content { width: fit-content; margin: auto; }" +
"</style>";
return styles;
},
/**
* This function will check if the sys_id is for a valid user
* @param {string} userSysId - sys_id of sys_user
*/
verifyEmail: function(userSysId) {
var userGr = new GlideRecord(this.constants.tables.SYS_USER);
return userGr.get(userSysId);
},
/**
* This function will create the appropriate link for the specific event
* @param {string} type - The type of security event
* @param {object} data - The data object that will contain each event's specific information
*/
getLink: function(type, data) {
var link = gs.getProperty(this.constants.property.SERVLET_URI);
var secDashEvents = {
failedLogins: this.constants.dashEvents.LOGIN_FAILED,
externalLogins: this.constants.dashEvents.LOGIN_EXTERNAL,
securityElevations: this.constants.dashEvents.SECURITY_ELEVATION,
sncLogins: this.constants.dashEvents.LOGIN_SNC,
adminLogins: this.constants.dashEvents.LOGIN_ADMIN,
impersonations: this.constants.dashEvents.IMPERSONATION,
exports: this.constants.dashEvents.EXPORT,
};
// Get the date a week ago from the event
var gdt = new GlideDateTime(data.time);
gdt.addDays(-7);
var lastWeekDateTime = gdt.getValue();
// Return link based on event/report combination
if (type === this.constants.events.LOGIN_FAILED_NOTIFICATION) {
var userName = data.userName,
time = data.time;
link += 'isc?id=security_report_details&table=sysevent&filter=name=login.failed^parm1=' +
userName + '^sys_created_on<=' + time + '^ORDERBYDESCsys_created_on';
} else if(type === this.constants.events.IMPERSONATION_NOTIFICATION) {
var userName = data.userName,
time = data.time;
link += 'isc?id=security_report_details&table=sysevent&filter=name=impersonation.start^parm2=' +
userName + '^sys_created_on<=' + time + '^ORDERBYDESCsys_created_on';
} else if(type === this.constants.events.NEW_ADMIN_LOGIN_NOTIFICATION) {
var userName = data.userName,
time = data.time;
link += "isc?id=security_report_details&table=sysevent&filter=name=login^parm1=" + userName +
'^sys_created_on<=' + time + "^ORDERBYDESCsys_created_on";
} else if(type === this.constants.events.SECURITY_ELEVATION_NOTIFICATION) {
var userName = data.userName,
time = data.time;
link += 'isc?id=security_report_details&table=sysevent&filter=name=security.elevated_role.enabled^parm1='
+ userName + '^sys_created_on<=' + time + '^ORDERBYDESCsys_created_on';
} else if(type === this.constants.events.HP_ROLE_ADDED_NOTIFICATION) {
var userSysId = data.userSysId,
time = data.time;
link += 'isc?id=security_report_details&table=sys_user_has_role&filter=user=' +
userSysId + '^sys_created_on<=' + time + '^ORDERBYDESCsys_created_on';
} else if(type === this.constants.events.ADMIN_UNLOCK_NOTIFICATION) {
var unlockerUserName = data.unlockerUserName,
time = data.time;
link += 'isc?id=security_report_details&table=sys_user&filter=sys_updated_by=' +
unlockerUserName + '^ORDERBYDESCsys_updated_on';
} else if (type === this.constants.events.EXPORT_NOTIFICATION) {
var exporterUserName = data.userName,
time = data.time;
link += 'isc?id=security_report_details&table=isc_export_event&filter=user.name=' +
exporterUserName + '^sys_created_on<=' + time + '^classificationISNOTEMPTY^ORDERBYDESCsys_updated_on';
} else if(type === this.constants.events.WEEKLY_DIGEST_NOTIFICATION) {
var id = data.id,
time = data.time;
switch(id) {
case 'failedLogins':
case 'externalLogins':
case 'securityElevations':
case 'sncLogins':
case 'adminLogins':
case 'impersonations':
case 'exports':
var eventTable = this.constants.dashEventToTable[secDashEvents[id]];
var additionalQuery = this._getWeeklyDigestEventFilter(secDashEvents[id], eventTable);
link += 'isc?id=security_report_details&table=' + eventTable + '&filter=sys_created_on>' + lastWeekDateTime +
'^sys_created_on<=' + time + '^ORDERBYDESCsys_created_on' + additionalQuery;
break;
case 'quarantinedFiles':
case 'virusTypes':
link += 'isc?id=security_report_details&table=quarantined_file&filter=sa_sys_created_on>'
+ lastWeekDateTime + '^sa_sys_created_on<=' + time + '^ORDERBYDESCsys_created_on';
break;
case 'adminUsersAdded':
link += 'isc?id=security_report_details&table=sys_user_has_role&filter=role.name=admin^sys_created_on>'
+ lastWeekDateTime + '^sys_created_on<=' + time + '^ORDERBYDESCsys_created_on';
break;
case 'spamEmails':
link += 'isc?id=security_report_details&table=sys_email&filter=mailbox.name=Junk^sys_created_on>'
+ lastWeekDateTime + '^sys_created_on<=' + time + '^ORDERBYDESCsys_created_on';
break;
case 'inactiveHpUsers':
gdt = new GlideDateTime(time);
gdt.addDays(-30);
var timeThirtyDaysAgo = gdt.getValue();
link += 'isc?id=security_report_details&table=sys_user&filter=last_login_time<' + timeThirtyDaysAgo
+ '^roles=admin^ORroles=security_admin^ORroles=oauth_admin^ORroles=impersonator^ORDERBYDESClast_login_time';
break;
default:
break;
}
}
return link;
},
/**
* This function will convert the time passed to it to the lexical month/day time
* @param {GlideDateTime} gdt - The GlideDateTime object to be converted to lexical time
*/
convertToLexicalTime: function(gdt) {
var monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
var month = monthNames[gdt.getMonthLocalTime() - 1];
var day = gdt.getDayOfMonthLocalTime();
return month + ' ' + day;
},
/**
* This function will generate the appropriate template for the messaging channel found
* @param {GlideRecord} current - The glide record attached to the event queue
* @param {GlideRecord} event - This contains the event information from the event fired
* @param {any} funcs - This contains the functions used to generate the specific notification information
*/
generateNotification: function(current, event, funcs) {
// Current record information
var that = this;
var userSysId = current.user.sys_id.toString();
var data = current.data.toString();
var dateCreated = current.sys_created_on.toString();
var notificationName = this.eventNameToNotifPref[current.event.toString()];
// Get user and generate notification
var userGr = new GlideRecord(this.constants.tables.SYS_USER);
var foundUser = userGr.get(userSysId);
if(foundUser) {
// Get data to populate template
var templateData = funcs.getTemplateData(userGr, data, dateCreated);
// Check which messaging channel to generate for. No event defined means email,
// otherwise it must be slack or teams
if (event) {
funcs.generateEmailTemplate(templateData);
} else {
// Check which notification type is enabled
var type = _checkNotificationType(userGr, notificationName);
funcs.generateMessagingTemplate(templateData, type);
}
}
// This function will verify the type of messaging notification the user has enabled (Slack/Teams)
function _checkNotificationType(userGr, notificationName) {
var userPrefsQuery = "user=" + userGr.sys_id + "^notification.nameLIKE" + notificationName +
"^device.nameLIKEslack^ORdevice.nameLIKEteams^notification_filter=^ORDERBYDESCsys_updated_on";
var userNotificationSettingsGr = new GlideRecord(that.constants.tables.USER_NOTIF_PREFS);
userNotificationSettingsGr.addEncodedQuery(userPrefsQuery);
userNotificationSettingsGr.query();
while(userNotificationSettingsGr.next()) {
// Check for the one that is active
var deviceName = userNotificationSettingsGr.device.name.toString().toLowerCase();
var deviceActive = userNotificationSettingsGr.device.active.toString();
if (deviceActive === "true") {
if (deviceName.contains(that.constants.messagingTypes.SLACK)) {
return that.constants.messagingTypes.SLACK;
} else if (deviceName.contains(that.constants.messagingTypes.TEAMS)) {
return that.constants.messagingTypes.TEAMS;
}
}
}
return null;
}
},
/**
* This function will generate an activity and unsubscribe link for the messaging notifications
* @param {any} templateData - The data to populate the template
* @param {string} type - The messaging channel type
*/
generateMessagingFooter: function(templateData, type) {
// Generate footer message
var footerMessage = "";
var link = gs.getProperty(this.constants.property.SERVLET_URI);
if (type === this.constants.messagingTypes.SLACK) {
footerMessage = "<" + encodeURI(templateData.link) + "|View Activity>"
+ "\t<" + encodeURI(link + this.constants.UNSUBSCRIBE_ROUTE) + "|Unsubscribe>";
} else if (type === this.constants.messagingTypes.TEAMS) {
footerMessage = "</br><a href='" + encodeURI(templateData.link) + "'>View Activity</a> <a href='" +
encodeURI(link + this.constants.UNSUBSCRIBE_ROUTE) + "'>Unsubscribe</a>";
}
return footerMessage;
},
/**
* This function will add on any additional query parameters if an event
* requires additional filtering
* @param {string} event - ISC event type
* @param {string} table - event table
*/
_getWeeklyDigestEventFilter: function(event, table) {
if (table === this.constants.tables.ISC_LOGIN) {
return '^login_type=' + event;
}
return '';
},
type: 'ISCNotificationTemplateUtility'
};
ISCNotificationTemplateUtility.prototype.weeklyDigestTitles = {
failedLogins: 'Failed Logins',
externalLogins: 'External Logins',
securityElevations: 'Security Elevations',
sncLogins: 'SNC Logins',
adminLogins: 'Admin Logins',
quarantinedFiles: 'Quarantined Files',
virusTypes: 'Virus Types',
adminUsersAdded: 'Admin Users Added',
impersonations: 'Impersonations',
spamEmails: 'Spam Emails',
inactiveHpUsers: 'Inactive High Privilege Users',
exports: 'Exports'
};
ISCNotificationTemplateUtility.prototype.weeklyDigestDesc = {
failedLogins: 'Number of attempted logins that failed',
externalLogins: 'Number of successful user logins with snc_external role',
securityElevations: 'Number of times that an admin has elevated to security_admin role',
sncLogins: 'Number of ServiceNow logins',
adminLogins: 'Number of successful user logins with admin role',
quarantinedFiles: 'Number of files that were quarantined from running Antivirus Scan',
virusTypes: 'Number of different virus types found during antivirus scan',
adminUsersAdded: 'Number of users with an admin role that were added',
impersonations: 'Number of impersonation login',
spamEmails: 'Number of incoming emails to the instance marked as spam',
inactiveHpUsers: 'Number of high privilege users not logged in the past 30 days',
exports: 'Number of exports'
};
ISCNotificationTemplateUtility.prototype.eventNameToNotifPref = {
'appsec.notification.login.failed': 'Failed Login',
'appsec.notification.impersonation': 'Impersonation',
'appsec.notification.login.new_ip': 'Admin Login',
'appsec.notification.security.elevation': 'Security Elevation',
'appsec.notification.weekly_digest': 'Weekly Digest',
'appsec.notification.hp_role_added': 'HP Role Added',
'appsec.notification.admin_unlock': 'Admin Unlock',
'appsec.notification.export': 'Export'
};
Sys ID
16a5c5080fc30010b25fea12ff767e79