How to Optimize Trigger Code?

I have many customers using extrahop to search for Expired SSL Certs. Customers ask me for an easier way to find the Certs they are looking for from some kind of external list.

After speaking with support I have been asked to create a trigger that creates an Application Container:

Im looking for help to optimize the code below:

var myApp = Application("My App: SSL CERTS");
if (SSL.certificate === null) {
  return;
}

// a list for the customer to input cert subjects
  
var CertSubjectList = {
    Subject: "extrahop.com",
    Subject2: "extrahop",
    Subject3: "www.extrahop.com",
    Subject4: "EH",
    SubjectN: "Reveal"
};
const keys = Object.keys(CertSubjectList);
const value = Object.values(CertSubjectList);

//debug(" [keys test1]:" + keys);
//debug(" [values test2]:" + value);
// var subjectOfInterest1 = "extrahop.com";
// var subjectOfInterest2 = "extrahop";

var subject = SSL.certificate.subject;
if (subject !== null) {
function contains(target, pattern){
   var value = 0;
 
   pattern.forEach(function(word){
     value = value + target.includes(word);
   });
   if (value === 1){}
   return target;
 }
}
  var newSubject = contains(subject, value);
  //debug("[values test3]:" + newSubject);
  var host = SSL.certificate.issuer;
  if (newSubject !== false) {
      var advanceDaysNotice = 90;
      var now = Date.now() / 1000; 
      if ((now + advanceDaysNotice * 24 * 60 * 60) > SSL.certificate.notAfter) {
           myApp.metricAddCount('expiring_certs_Count', 1);
           myApp.metricAddDetailCount('expiring_certs_Host', host, 1);
           myApp.metricAddDetailCount("Cert_Subject", subject , 1);
           myApp.metricAddDetailCount('expiring_certs_Expiration_in_Days', "["+host+"] Expires in " + ~~((SSL.certificate.notAfter - now) / 86400) + " days", 1);
      //    debug(" Host: " + host + " Cert: " + subject + "\nExpires in " + ~~((SSL.certificate.notAfter - now) / 86400) + " days");       
      }
  }

Here is the dashboard example:

Conclusion

  • I am new to programing please be gentle with my question here.
  • Please feel free to share with me any JS books/blog you recommend on Optimization of code or send me in any other direction in study.

This information is effectively already in the product via a built-in metric.

  1. Click on ‘Assets’ in the top nav
  2. Click on ‘Activity’ in the left nav
  3. Select ‘N servers’ from the SSL line in the list
  4. In the region named ‘SSL Certificate Details’, click the ‘Certificate Expiration Dates’ and then select the menu option ‘View more Certificates’.
    view_more_certs
  5. The resulting page presents you with a LOT of information related to expired and expiring certs, including their expiration dates, connections, and bytes transferred. You can also apply filters on this page to limit the results to your liking.
  6. If you want this in a dashboard, click the vertical-dot menu in the top right of the window, and click ‘Create Chart’.
    image

Cheers!

1 Like

Hi @ryanc,

How would you recommend filtering 20 Plus certs in the UI? Next, any comments on the trigger code and how to optimize it? I will create some feature request later this week on what some customers are struggling with from the UI. For this post, I am looking to develop my scripting skills and clear technical communication with the customer. All help to my efforts are most welcome :slight_smile:

@rainbird I modified the code you had. What do you think of the following?

/* Detect soon-to-expire SSL certificates
 * Author: ExtraHop.com, modified by cal@extrahop.com -> modified by keshb@extrahop.com
 * Event: SSL_OPEN
 */
var myApp = Application("My App: SSL CERTS");
var cert = SSL.certificate;
if (cert == null) {
  return;
}

if (relevantSubject(cert.subject)) {

    
    let advanceDaysNotice = 90;
    let secondsPerDay = 60 * 60 * 24;

    // seconds since Unix epoch
    let now = (Date.now() / 1000);

     if (now + advanceDaysNotice * secondsPerDay > cert.notAfter) {
        myApp.metricAddCount('expiring_certs_Count', 1);
        myApp.metricAddDetailCount('expiring_certs_Host', cert.issuer, 1);
        myApp.metricAddDetailCount("Cert_Subject", cert.subject , 1);
        myApp.metricAddDetailCount('expiring_certs_Expiration_in_Days', "["+cert.issuer+"] Expires in " + ~~((cert.notAfter - now) / secondsPerDay) + " days", 1);
        debug("-- Testing --" + " Host:" + cert.issuer + " Cert: " + cert.subject + "\nExpires in " + ~~((cert.notAfter - now) / secondsPerDay) + " days");
    }

}

function relevantSubject(subject) {
    // Adjust to catch CN's of interest. */  
    let subjects = [
        "extrahop.com",
        "extrahop",
        "www.extrahop.com",
        "EH",
        "Reveal"
    ];
    for (let s in subjects) {
        if (subject.includes(s)) {
            return true;
        }
    }
    return false;
}
1 Like

Very clean! I am testing your code now.

  • you lowered the trigger load
  • and stoped one of my error

Thanks for the input @keshb

I Spoke with An ExtraHop SA today And got some feedback on the code:

  1. Currently, We are looping on an int. we should change the “in” operation to a “of” and we return at the end of the loop a strings not ints.This change has been updated in the code now and it works great!

  1. In the code we are using date.now(). We may be able to use get TimestampMsec(). This may improve performance…it may not. I will test.

  1. Currently we are looping through an array looking for PARTS of a string. If we want to look for Exact strings we can simplify the code and exclude the function releventSubject().

  1. SSL Subjects is not the only thing we can search using extrahop. There is another method we may want to add to the code. By adding subjectAlternativeNames to the code we may gain more visibility to certs with extrahop.

New Script

/* Detect soon-to-expire SSL certificates

* Author: ExtraHop.com, modified by cal@extrahop.com -> modified by keshb@extrahop.com

* Event: SSL_OPEN

*/

var myApp = Application("My App: SSL CERTS");

var cert = SSL.certificate;

if (cert == null) {

return;

}

if (relevantSubject(cert.subject)) {

let advanceDaysNotice = 90;

let secondsPerDay = 60 * 60 * 24;

// seconds since Unix epoch

let now = (Date.now() / 1000);

if (now + advanceDaysNotice * secondsPerDay > cert.notAfter) {

//not working right now. Recommended to use "AddDistinct()" here.
myApp.metricAddDistinct('unique_expiring_certs', cert.subject);

 // got rid of 'Host metric" its out of scope for the customers needs
myApp.metricAddDetailCount("Cert_Subject", cert.subject , 1);

myApp.metricAddDetailCount('expiring_certs_Expiration_in_Days', "["+cert.issuer+"] Expires in " + ~~((cert.notAfter - now) / secondsPerDay) + " days", 1);

// working! Recommended to use "AddDetailSnap' here. We now have counts to expire as a metric
myApp.metricAddDetailSnap('expiring_certs_Snap_metric', cert.subject, (cert.notAfter - now) / secondsPerDay);

debug("-- Testing --" + " Host:" + cert.issuer + " Cert: " + cert.subject + "\nExpires in " + ~~((cert.notAfter - now) / secondsPerDay) + " days");

}

}

function relevantSubject(subject) {

// Adjust to catch CN's of interest. */

let subjects = [

"extrahop.com",

"extrahop",

"www.extrahop.com",

"EH",

"Reveal"

];

for (let s of subjects) {

if (subject.includes(s)) {

return true;

}

}

return false;

}

I did some quick hacking and identified a few places where performance could be improved somewhat dramatically. Here is a comparison of your latest trigger, which uses a loop and includes(), with a modified version that that has no loop and utilizes a cached regular expression.

Key takeaways:

  • Utilize our cache function when you have several variables that only change when the trigger code changes (regexes, calculated values that don’t depend on something out of the flow, arrays/objects with more than a few values, etc)
  • Use const instead of let and var for variables if the value is never reassigned.
  • If a value is calculated or made more than once, save that in a variable (e.g. the original code was calculating the days until expiration multiple times).
  • Use getTimestamp() instead of Date, as this reflects the timestamp of the packet that caused the Trigger to fire, rather than the time that the trigger code is executing.

Code –

const subjects = [
    'extrahop.com',
    'extrahop',
    'www.extrahop.com',
    'EH',
    'Reveal'
];

const cert     = SSL.certificate;
const criteria = cache('CRITERIA', () => ({
    'days_notice'     : 90,
    'seconds_per_day' : 86400,
    're'              : new RegExp(subjects.join('|'), 'i')
}));


if ( cert === null || cert.subject === null ) {
    return;
}

if ( criteria.re.test(cert.subject) ) {
    const now         = getTimestamp() / 1000;
    const cutoff_date = now + ( criteria.days_notice * criteria.seconds_per_day );

    if ( cutoff_date > cert.notAfter ) {
        const expires = parseInt(( cert.notAfter - now ) / criteria.seconds_per_day);
        
        debug('Matched on '    + cert.subject + 
              ', issued by: '  + cert.issuer + 
              ', expires in: ' + expires      + ' days');

        // Add metrics here
    }
}
1 Like

Thanks @ryanc! :slight_smile: