Skip to main content
BeyondTrust Employee
September 28, 2026

Tenable & ServiceNow Integration with Endpoint Privilege Management Using Vulnerability Scores

  • September 28, 2026
  • 0 replies
  • 5 views

 Description of Integration:

The integration described in this guide uses webhooks and API requests to allow users to approve EPM BeyondTrust application access request tickets in ServiceNow using CVE scores and severity ratings from Tenable, which eliminates manual intervention and delays, helping organizations improve their overall security posture.

Disclaimer
This integration is an example of a working prototype and will not be supported by BeyondTrust. Any sample or proof of concept code (“Code”) provided on the Community is provided “as is” and without any express or implied warranties. This means that we do not promise that it will work for your specific needs or that it is error-free. Such Code is community supported and not officially supported by BeyondTrust. BeyondTrust and its contributors are not liable for any damage you or others might experience from using the Code, including but not limited to, loss of data, loss of profits, or any interruptions to your business, no matter what the cause is, even if advised of the possibility of such damage. 

 

The required permissions in the BeyondTrust Product to be able to configure the integration:

Access to a Privilege Management tenant is required and an account with the Administrator role.

The required permissions/access level in ServiceNow to be able to configure the integration:

  • An active BeyondTrust Privilege Management (EPM) API account
    • The API account will require Full Access to the Command API. 
    • OAuth Provider (BeyondTrust EPM OAuth) in the ServiceNow Application Registry configured with your Endpoint Privilege Management settings: 
    • Client ID and Secret of your Endpoint Privilege Management (EPM) Access API account 
    • For the token URL, add your Endpoint Privilege Management (EPM) instance 
  • ServiceNow instance with Flow Designer and Security Incident Response application enabled.
  • Tenable API credential alias named Tenable_api already configured in ServiceNow (Connection & Credential Aliases). This requires you to create your connection and add your API Key Credentials using the credentials generated by Tenable.

Setting Up a Webhook in ServiceNow to Receive BeyondTrust Ticket Details:

Create a Webhook in EPM - Privilege Management Console:

You will create webhooks for each event type you want to integrate with ServiceNow.

Webhook URL

The webhook URL for all webhooks will follow this pattern:

https://YOUR-INSTANCE.service-now.com/api/x_bets_pmc_jit/authorization_request_webhook_handler

Replace YOUR-INSTANCE with your actual ServiceNow instance subdomain.

Example: https://acme.service-now.com/api/x_bets_pmc_jit/authorization_request_webhook_handler

Create webhooks

  1. Navigate to: EPM → Configuration → Webhook Settings
  2. Click Create Webhook

Webhook 1: JIT Admin Ticket Created

Configure the first webhook with the following settings:

  • Name: ServiceNow - Admin Access - Create
  • URL: https://YOUR-INSTANCE.service-now.com/api/x_bets_pmc_jit/authorization_request_webhook_handler
  • Event: JIT Admin Ticket Created
  • Content Type: application/json
  • Template: Use the template below
  • Authentication Type: Basic
  • Authentication Username: pmc_webhook_user (or the username you created)
  • Authentication Password: The password you set for the webhook user
  • Enable Webhook: Check to enable

Template for JIT Admin Ticket Created:

JSON

{
"EventType": "JIT_ADMIN_TICKET_CREATED",
"RequestId": "%%RequestId%%",
"TenantId": "%%TenantId%%",
"Timestamp": "%%Timestamp%%",
"ComputerId": "%%ComputerId%%",
"GroupId": "%%GroupId%%",
"TicketNumber": "%%TicketNumber%%",
"Reason": "%%Reason%%",
"UserId": "%%UserId%%",
"UserName": "%%UserName%%",
"DurationRequested": "%%DurationRequested%%"
}

Click Save.

Webhook 2: JIT Admin Decision Updated

Create a second webhook with the following settings:

  • Name: ServiceNow - Admin Access - Update
  • URL: https://YOUR-INSTANCE.service-now.com/api/x_bets_pmc_jit/authorization_request_webhook_handler
  • Event: JIT Admin Decision Updated
  • Content Type: application/json
  • Template: Use the template below
  • Authentication Type: Basic
  • Authentication Username: pmc_webhook_user
  • Authentication Password: The password you set for the webhook user
  • Enable Webhook:  Check to enable

Template for JIT Admin Decision Updated:

JSON

{
"EventType": "JIT_ADMIN_TICKET_UPDATED",
"RequestId": "%%RequestId%%",
"TicketNumber": "%%TicketNumber%%",
"TenantId": "%%TenantId%%",
"StartTime": "%%StartTime%%",
"EndTime": "%%EndTime%%",
"Decision": "%%Decision%%",
"DecisionTime": "%%DecisionTime%%",
"DecisionPerformedByUser": "%%DecisionPerformedByUser%%",
"Duration": "%%Duration%%",
"DurationRequested": "%%DurationRequested%%",
"Timestamp": "%%Timestamp%%"
}

Click Save.

Webhook 3: JIT Application Access Ticket Created

Create a third webhook with the following settings:

  • Name: ServiceNow - App Access - Create
  • URL: https://YOUR-INSTANCE.service-now.com/api/x_bets_pmc_jit/authorization_request_webhook_handler
  • Event: JIT Application Access Ticket Created
  • Content Type: application/json
  • Template: Use the template below
  • Authentication Type: Basic
  • Authentication Username: pmc_webhook_user
  • Authentication Password: The password you set for the webhook user
  • Enable Webhook: Check to enable

Template for JIT Application Access Ticket Created:

JSON

{
"EventType": "URM_Native_Ticket_Created",
"RequestId": "%%RequestId%%",
"TenantId": "%%TenantId%%",
"Timestamp": "%%Timestamp%%",
"TicketNumber": "%%TicketNumber%%",
"requestInfo": {
"applicationType": "%%ApplicationType%%",
"action": "%%Action%%",
"workstyle": "%%Workstyle%%",
"reason": "%%Reason%%",
"user": "%%User%%",
"hostName": "%%HostName%%",
"trustedOwnership": "%%trustedOwnership%%",
"productDescription": "%%ProductDescription%%",
"driveType": "%%DriveType%%",
"btZoneIdentifierExists": "%%BtZoneIdentifierExists%%",
"productCode": "%%ProductCode%%",
"upgradeCode": "%%UpgradeCode%%",
"clsId": "%%ClsId%%",
"comDisplayName": "%%ComDisplayName%%",
"token": "%%Token%%",
"tokenAssignmentIsShell": "%%TokenAssignmentIsShell%%",
"uacTriggered": "%%UacTriggered%%",
"downloadSourceUrl": "%%DownloadSourceUrl%%",
"userLanguage": "%%UserLanguage%%",
"sha1Hash": "%%Sha1Hash%%",
"sha256Hash": "%%Sha256Hash%%",
"filePathObjectId": "%%FilePathObjectId%%",
"cmdArgs": "%%CmdArgs%%",
"publisher": "%%Publisher%%",
"productName": "%%ProductName%%",
"productVersion": "%%ProductVersion%%",
"fileVersion": "%%FileVersion%%",
"workstyle": "%%Workstyle%%",
"applicationGroup": "%%ApplicationGroup%%",
"message": "%%Message%%",
"messageId": "%%MessageId%%",
"hostType": "%%HostType%%",
"osName": "%%OsName%%",
"osVersion": "%%OsVersion%%",
"ComputerGroup": "%%ComputerGroup%%",
"GroupId": "%%GroupId%%",
"requestVersion": "%%RequestVersion%%",
"hostedFilePath": "%%HostedFilePath%%",
"parentProcess": "%%ParentProcess%%",
"storeAppName": "%%StoreAppName%%",
"serviceName": "%%ServiceName%%",
"serviceAction": "%%ServiceAction%%",
"authRequestUri": "%%AuthRequestUri%%",
"bundleInfoDescription": "%%BundleInfoDescription%%",
"reputationScoreDateTime": "%%ReputationScoreDateTime%%",
"reputationScore": "%%ReputationScore%%",
"reputationIntegrationType": "%%ReputationIntegrationType%%"
}
}

Click Save.

Webhook 4: JIT Application Access Decision Updated

Create a fourth webhook with the following settings:

  • Name: ServiceNow - App Access - Update
  • URL: https://YOUR-INSTANCE.service-now.com/api/x_bets_pmc_jit/authorization_request_webhook_handler
  • Event: JIT Application Access Decision Updated
  • Content Type: application/json
  • Template: Use the template below
  • Authentication Type: Basic
  • Authentication Username: pmc_webhook_user
  • Authentication Password: The password you set for the webhook user
  • Enable Webhook: Check to enable

Template for JIT Application Access Decision Updated:

JSON

{
"EventType": "URM_NATIVE_DECISION_UPDATE",
"RequestId": "%%RequestId%%",
"TenantId": "%%TenantId%%",
"TicketNumber": "%%TicketNumber%%",
"Timestamp": "%%Timestamp%%",
"requestInfo": {
"Decision": "%%Decision%%",
"DecisionTime": "%%DecisionTime%%",
"DecisionPerformedByUser": "%%DecisionPerformedByUser%%",
"Duration": "%%Duration%%"
}
}

Click Save.

How to test and troubleshoot the integration:

  • To test and invoke the webhook, perform the following instructions:
    • Click “Send Sample Event” and you should see a message “Sample event sent successfully” 

Create the Actions and Flow in ServiceNow

Part 1: Create Action — "Get Tenable Vulnerabilities and Scores"

1.1  Create the Action

Step 1: In Tenable, navigate to Flow Designer.

Step 2: Click New > Action.

 

 

Step 3: Set the following properties:

Action Name

Get Tenable Vulnerabilities and Scores

Application

Security Incident Response

Description

Calls Tenable Workbench Vulnerabilities API and returns CVE findings count, max severity, max VPR score, Plugin Name, and max CVSS score for the application in the incident description.

 

 

Step 4: Click Submit to create the action.

 

1.2  Define Action Inputs

Step 1: In the Action Outline (left panel), click Inputs.

Step 2: Click Create Variable and add the following input:

 

Label

Incident Description

Name

incident_description

Type

String

Mandatory

No

 

 

1.3  Add Step 1 — REST Step (Spotlight API Call)

Step 1: Click the + icon in the Action Outline to add a new step.

Step 2: Search for and select REST.

 

 

Step 3: Configure the REST step as follows:

 

Connection

Define Connection Inline

Credential Alias

Tenable_api

Base URL

https://cloud.tenable.com

HTTP Method

GET

Resource Path

/workbenches/vulnerabilities?date_range=90

Request Body

(leave empty)


 

Step 4: Under Headers, add:

 

Content-Type

application/json

Accept

application/json

 

Step 5: Save the step. The step will output Response Body, Status Code, and Error Code.

 

 

1.4  Add Step 2 — Script Step (Parse and Match CVEs)

Step 1: Click the + icon to add another step.

Step 2: Search for and select Script.

Step 3: Set Required Runtime to Instance.

 

Configure Input Variables for the Script Step

In the Input Variables section of the script step, create two variables:

 

Name

Value (pill mapping)

incident_description

Action Input > Incident Description

response_body

Step 1 (REST step) > Response Body

 

Note: The variable name in this table (left column) is the name you type in the Name field. The right column is what you drag/select from the Data panel on the right side of the screen. The variable name must exactly match what the script uses in inputs.incident_description and inputs.response_body.

 

 

Configure Output Variables for the Script Step

In the Output Variables section at the bottom of the script step, create three variables:

Label

Name

Finding Count

finding_count

Max CVSS Score

max_cvss_score

Max Severity

max_severity

Max VPR Score

max_vpr_score

Plugin Name

plugin_name

Label

Name

Type

Finding Count              

finding_count

String

Max CVSS Score

max_cvss_score

String

Max Severity

max_severity

String

Max VPR Score

max_vpr_score

String

Plugin Name

plugin_name

String

 

 

Enter the Script

Paste the following script into the Script field:

(function execute(inputs, outputs) {



    function setOut(sev, cvss, vpr, plugin, count) {

        outputs.max_severity   = sev;

        outputs.max_cvss_score = cvss;

        outputs.max_vpr_score  = vpr;

        outputs.plugin_name    = plugin;

        outputs.finding_count  = count;

    }



    var body = JSON.parse(inputs.response_body);

    if (!body || !body.vulnerabilities) { setOut('API error','','','','0'); return; }

    var all = body.vulnerabilities;



    // 1. Application path -> search terms (handles spaces in the path)

    var p = (inputs.incident_description || '')

            .match(/[a-zA-Z]:\\[^\r\n]*?\.(?:exe|dll|msi|msp|bat|cmd|com|ps1|vbs|scr|jar)\b/i);



    if (!p) { setOut('no path in description','0','0','','0'); return; }



    var full   = p[0].toLowerCase().replace(/\//g, '\\');

    var parts  = full.split('\\');

    var file   = parts.pop().replace(/\.[^.]+$/, '');   // notepad++.exe -> notepad++

    var folder = parts.pop() || '';                     // notepad++



    // generic folders tell us nothing about the product

    var GENERIC = ['windows','system32','syswow64','winsxs','temp','tmp','bin','downloads'];

    if (GENERIC.indexOf(folder) >= 0) folder = '';



    function strip(s) {

        return String(s).replace(/^(ms|msft)/, '').replace(/[^a-z0-9]/g, '');

    }



    var exact = [], loose = [];

    [file, folder].forEach(function (b) {

        if (!b || b.length < 3) return;

        if (exact.indexOf(b) < 0) exact.push(b);          // notepad++

        var s = strip(b);

        if (s.length >= 3 && loose.indexOf(s) < 0) loose.push(s);   // notepad

    });



    if (!exact.length) { setOut('no usable term','0','0','','0'); return; }



    // 2. Match on plugin name only - exact terms first, then relaxed

    function findMatches(terms) {

        var out = [];

        for (var i = 0; i < all.length; i++) {

            var name = String(all[i].plugin_name || '').toLowerCase();

            for (var t = 0; t < terms.length; t++) {

                if (name.indexOf(terms[t]) >= 0) { out.push(all[i]); break; }

            }

        }

        return out;

    }



    var matched = findMatches(exact);

    if (!matched.length) matched = findMatches(loose);

    if (!matched.length) { setOut('not found','0','0','','0'); return; }



    // 3. Summarise

    var LABEL = ['INFO','LOW','MEDIUM','HIGH','CRITICAL'];

    var maxCvss = 0, maxVpr = 0, maxSevIdx = -1, topPlugin = '';



    for (var k = 0; k < matched.length; k++) {

        var m = matched[k];



        var cvss = (m.cvss3_base_score != null) ? m.cvss3_base_score

                 : (m.cvss_base_score  != null) ? m.cvss_base_score : 0;

        cvss = parseFloat(cvss) || 0;

        if (cvss > maxCvss) { maxCvss = cvss; topPlugin = m.plugin_name || ''; }



        var vpr = parseFloat(m.vpr_score) || 0;

        if (vpr > maxVpr) maxVpr = vpr;



        var sev = (typeof m.severity === 'number') ? m.severity : -1;

        if (sev > maxSevIdx) maxSevIdx = sev;

    }



    setOut(LABEL[maxSevIdx] || 'INFO', String(maxCvss), String(maxVpr),

           topPlugin, String(matched.length));

})(inputs, outputs);

 

How the Script Works

  • Regex pulls the application path out of the description, matching from a drive letter through to a known executable extension — that's what lets it handle spaces like c:\program files\notepad++\.
  • Splits the path into filename (notepad++) and parent folder (notepad++), dropping the extension.
  • Blocklists generic folders (windows, system32, temp…) so a path's location doesn't get mistaken for a product name.
  • Builds two tiers of terms: exact keeps the name as-is (notepad++), loose strips punctuation and vendor prefixes (notepad, msedge → edge).
  • Scans every plugin's plugin_name for any exact term first and matching on plugin_name only.

 

Step 4: Click Save.

Step 5: Click Publish to make the action available in flows.

 

Part 2: Create Action — "Add Tenable CVSS Scores and CVE Score to Incident Work Notes"

2.1  Create the Action

Step 1: In Flow Designer, click New > Action.

Step 2: Set the following properties:

 

Action Name

Add Tenable CVSS Scores and CVE Score to Incident Work Notes

Application

Security Incident Response

Description

Writes Tenable CVE findings (count, severity, CVSS score, and VPR Score) to the incident work notes.

 

Step 3: Click Submit.

 

2.2  Define Action Inputs

Step 1: Click Inputs in the Action Outline.

Step 2: Create four input variables:
 

Label

Name

Type

Incident Number

incident_number

String

Finding Count              

finding_count

String

Max CVSS Score

max_cvss_score

String

Max Severity

max_severity

String

Max VPR Score

max_vpr_score

String

Plugin Name

plugin_name

String

 

 

2.3  Add Step 1 — Script Step

Step 1: Click + to add a step and select Script.

Step 2: Set Required Runtime to Instance.

 

Configure Input Variables

Add the following input variables, mapping each to the corresponding action input:

 

incident_number

Action Input > Incident Number

finding_count

Action Input > Finding Count

max_severity

Action Input > Max Severity

max_cvss_score

Action Input > Max CVSS Score

max_vpr_score

Action Input > Max VPR Score

plugin_name

Action Input > Plugin Name

 

Note: This action has no output variables — it only writes to the incident and does not return values.

 

 

Enter the Script:
 

(function execute(inputs, outputs) {



    if (!inputs.incident_number) { return; }



    var gr = new GlideRecord('incident');

    gr.addQuery('number', inputs.incident_number);

    gr.query();

    if (!gr.next()) { return; }



    var sev = String(inputs.max_severity || '').toLowerCase();

    var noMatch = (sev === 'not found' || sev === 'api error' ||

                   sev === 'no usable term' || sev.indexOf('no path') === 0);



    var note;



    if (noMatch) {

        note = 'Tenable Vulnerability Report\n\n' +

               'No matching Tenable findings for the requested application.\n' +

               'Reason: ' + (inputs.max_severity || 'unknown');

    } else {

        function line(label, value) {

            value = (value === null || value === undefined) ? '' : String(value).trim();

            return value ? (label + ': ' + value + '\n') : '';

        }



        note = 'Tenable Vulnerability Report\n\n' +

               line('Highest Severity',   inputs.max_severity)   +

               line('Highest CVSS Score', inputs.max_cvss_score) +

               line('Highest VPR Score',  inputs.max_vpr_score)  +

               line('Findings Matched',   inputs.finding_count)  +

               line('CVEs Matched',       inputs.cve_count)      +

               line('Top Finding',        inputs.plugin_name);

    }



    gr.work_notes = note;

    gr.update();

})(inputs, outputs);

 

Step 3: Click Save, then Publish.

Part 3: Create a New Flow — "Deny EPM JIT App Request Based on Tenable Scores"

This section walks through building the full flow from scratch. The flow triggers when a BeyondTrust EPM JIT incident is created, retrieves CVE data from Tenable Spotlight, writes the findings to work notes, and — if the severity is HIGH or CRITICAL — automatically denies the EPM application and admin requests for the compromised user.

 

Completed flow structure:

 

Step

Action

Trigger

Incident Created where Short description contains "BeyondTrust EPM JIT"

1

Get Tenable Vulnerabilities and Scores

2

Add Tenable CVSS Scores and CVE Count to Incident Work Notes

3

If — Risk Score Criteria (Max Severity is HIGH OR CRITICAL)

4

Then — Deny EPM Application and Admin Requests for Compromised User

 

3.1  Create the Flow

Step 1: In Flow Designer, click New > Flow.

 

Step 2: Set the following properties:

 

Flow Name

Deny EPM JIT App Request Based on Tenable Scores

Application

Security Incident Response

Description

Triggers on BeyondTrust EPM JIT incident creation, retrieves Tenable CVE data, writes to work notes, and denies the EPM request if severity is HIGH or CRITICAL.

Run As

System User

 

 

Step 3: Click Submit.

 

3.2  Configure the Trigger

Step 1: In the flow canvas, click Add a Trigger.

Step 2: Select Record > Created.

Step 3: Configure the trigger:

 

Table

Incident [incident]

Condition field

Short description

Operator

contains

Value

BeyondTrust EPM JIT

 

Note: This condition ensures the flow only fires on BeyondTrust EPM JIT incidents — not every incident in the system. This prevents unnecessary Tenable API calls.

 

 

Step 4: Click Done.

 

3.3  Add Step 1 — Get Tenable Vulnerabilities and Scores

Step 1: Click the + icon below the trigger.

Step 2: Select Action.

Step 3: Search for and select "Get Tenable Vulnerabilities and Scores".

Step 4: Map the action input:

 

Incident Description

Trigger > Incident Record > Description

 

Note: The Description field contains the full BeyondTrust request string including the Windows file path (e.g., c:\windows\system32\mmc.exe). The action extracts the filename automatically.

 

 

Step 5: Click Done.

 

3.4  Add Step 2 — Write CVE Results to Work Notes

Step 1: Click + below Step 1.

Step 2: Select Action.

Step 3: Search for and select "Add Tenable CVSS Scores and CVE Count to Incident Work Notes".

Step 4: Map the action inputs:

 

Incident Number

Trigger > Incident Record > Number

Finding Count

Step 1 (Get Tenable Vulnerabilities and Scores) > Finding Count

Max Severity

Step 1 (Get Tenable Vulnerabilities and Scores) > Max Severity

Max CVSS Score

Step 1 (Get Tenable Vulnerabilities and Scores) > Max CVSS Score

Max VPR Score

Step 1 (Get Tenable Vulnerabilities and Scores) > Max VPR Score

Plugin Name

Step 1 (Get Tenable Vulnerabilities and Scores) > Plugin Name

 

 

Step 5: Click Done.

 

3.5  Add Step 3 — If Condition (Risk Score Criteria)

Step 1: Click + below Step 2.

Step 2: Select Flow Logic > If.

Step 3: Set the Condition Label to: Risk Score Criteria

Step 4: Add the following conditions:

 

Condition 1

Step 1 (Get Tenable Vulnerabilities and Scores) > Max Severity   is   HIGH

OR

 

Condition 2

Step 1 (Get Tenable Vulnerabilities and Scores) > Max Severity   is   CRITICAL

 

Note: Use the OR logic between conditions so that either HIGH or CRITICAL severity triggers the denial. Both conditions reference the Max Severity output from Step 1.

 

 

Step 5: Click Done.

 

3.6  Add Step 4 — Deny EPM Request (Then branch)

Step 1: Inside the "then" branch of the If condition, click the + icon.

Step 2: Select Action.

Step 3: Search for and select "Deny EPM Application and Admin Requests for Compromised User".

Step 4: Map the action input:

 

trackingRecord

Trigger > Record Created > Incident Record > Number

 

Note: The trackingRecord input links the denial action back to the originating incident. Map it from the trigger's Incident Record Number — not sys_id.

 

Step 5: Click Done.

 

3.7  Save and Activate

Step 1: Click Save in the top-right corner.

Step 2: Click Activate. The flow will now run automatically on every new BeyondTrust EPM JIT incident.