Forcepoint
Advanced threat protection with added local management controls.
Network Security · Forcepoint Web Security
Details
| ID | Forcepoint |
|---|---|
| Provider | Francisco Partners |
| Category | Network Security |
| From Version | 5.0.0 |
| Supported Modules | Agentix XSIAM Cortex Cloud EDR Cloud Runtime Security |
README
Forcepoint is an advanced threat protection product with added local management controls.
The Forcepoint-XSOAR integration allows you to create and manage custom categories.
To set up Forcepoint to work with Cortex XSOAR:
- Make sure you have administrator permissions.
- Make sure you have port 15873 open.
- Make sure you have Forcepoint API version 8.5.7 or later.
To set up the integration on Cortex XSOAR:
- Go to ‘Settings > Integrations > Servers & Services’
- Locate ‘Forcepoint ’ by searching for it using the search box on the top of the page.
- Click ‘Add instance’ to create and configure a new integration. You should configure the following settings:
Name: A textual name for the integration instance.
Server URL: API Server URL.
Username and Password: The username and password for accessing the integration.
Use system proxy settings: Specify whether to communicate with the integration with the system proxy server.
Do not validate server certificate: Select to circumvent server certification validation. You may want to do this in case the server you are connecting to does not have a valid certificate.
Forcepoint version 8.5.5 or later: Select if you have a hotfix/instrumental build that requires using POST requests instead of DELETE requests.
Cortex XSOAR engine: If relevant, select the engine that acts as a proxy to the server. Engines are used when you need to access a remote network segments and there are network devices such as proxies, firewalls, etc. that prevent the Cortex XSOAR server from accessing the remote networks.
For more information on Cortex XSOAR engines see:
Cortex XSOAR 6.13 - Engines
Cortex XSOAR 8 Cloud- Engines
Cortex XSOAR 8.7 On-prem - Engines - Press the ‘Test’ button to validate connection.
- After completing the test successfully, press the ‘Done’ button.
Fetched incidents data:
This integration does not fetch incidents.
Top Use-cases:
Forcepoint integration can be used to create a block list category for URL and IP addresses.
A possible flow of commands could be:
- Use ‘fp-add-category’ to add a new category. The new category will automatically be set to block access.
- Use ‘fp-get-category-details’ to get the new category ID.
- Use ‘fp-add-addresses-to-category’ to add URLs and/or IP addresses to the category. Use the category name or ID as identifier.
- Use ‘fp-delete-addresses-from-category’ to remove URLs and/or IP addresses from the category.
The integration can also be used to view a detailed list of managed categories. Use ‘fp-list-categories’ to view all Forceoint categories or only categories managed by the integration.
Commands:
- fp-list-categories
Input:
|
Option to list all categories or only API-managed categories (default). |
Context output:
|
``` { Forcepoint: { ListCategories: [ { CategoryDescription: Sites that provide information about or that sell or provide curriculum materials or direct instruction; also, learned journals and similar publications. CategoryID: 118 CategoryName: Educational Materials CategoryOwner: Forcepoint CategoryParent: Education } ] } ``` |
Raw output:
|
``` [ { Category Description: Parent category that contains categories known to consume bandwidth resources. Category Hierarchy: 890 Category ID: 116 Category Name: Bandwidth Category Owner: Forcepoint CategoryParent: Children: [ { Category Description: Sites that store personal files on Internet servers for backup or exchange. Category Hierarchy: 1510 Category ID: 113 Category Name: Personal Network Storage and Backup Category Owner: Forcepoint CategoryParent: Bandwidth } ] } ] ``` |
-
fp-get-category-details
Input:
|
category name or ID |
Context output:
|
{ Forcepoint: { CategoryDetails: { CategoryID: 116 CategoryName: Bandwidth IPs: [] URLs: [] } } } |
Raw output:
|
``` { Category ID: 116 Category Name: Bandwidth IPs: [] URLs: [] } ``` |
- fp-add-category
Input:
|
category name, category description, category parent. |
Context output:
|
``` { Forcepoint: { Add Category: { Categories: [ { Category Name: Test category } ] } } }
``` |
Raw output:
|
``` { Categories: [ { Category Name: Test category } ] } ``` |
- fp-add-addresses-to-category
Input:
|
Category name or ID , list of URLs and/or list of IP addresses. |
Context output:
|
``` { Forcepoint: { AddAddressToCategory: { Category ID: 1932 Category Name: Totals: { Added IPs: 0 Added URLs: 1 } } } } ``` |
Raw output:
|
``` { Category ID: 1932 Category Name: Totals: { Added IPs: 0 Added URLs: 1 } } ``` |
- fp-delete-addresses-from-category
Input:
|
category name or ID , list of URLs and/or list of IP addresses. |
Context output:
|
``` { Forcepoint: { AddAddressToCategory: { Category ID: 1932 Category Name: Totals: { Deleted IPs: 0 Deleted URLs: 1 } } } } ``` |
Raw output:
|
``` { Category ID: 1932 Category Name: Totals: { Deleted IPs: 0 Deleted URLs: 1 } } ``` |
- fp-delete-category
Input:
|
List of category names or IDs |
Context output:
|
``` { Forcepoint: { DeletedCategories: [ { CategoryID: 116 CategoryName: Bandwidth IPs: [] URLs: [] }, … ] } } ``` |
Raw output:
|
``` { Category ID: 116 Category Name: Bandwidth IPs: [] URLs: [] }, … ] ``` |
Additional info:
URL restrictions and clarifications:
- Only the hostname field (part of the authority) is required.
- Other parts are optional, but can be used to define a stricter match.
- CGI parameters (anything after the "?" in a URL) are automatically removed from the URL.
- If no protocol is specified, the following protocols will be added to the database:
http://, https://, and ftp://. - URLs can be added to more than one category. When the URL is matched, all categories for the URL are returned for use in policy enforcement.
IP addresses restrictions and clarification:
- IP addresses and ranges are as specified by IPv4 and IPv6.
- IP addresses and ranges can be added to more than one category. When an IP address is matched, all categories for the IP address are returned for use in policy enforcement.
Known Limitations
- New category will automatically be set to block access. You may change category access with Forcepoint TRITON manager.
- Since the API does not support TLS 1.2, the integration works only with TLS versions 1.0 and below.
Troubleshooting
-
Failed attempts to add/delete URL or IP addresses to a category might be caused by invalid category name or ID.
Invalid category name/ID indicates one of the following:- The category does not exist.
- The ID/name belongs to a Forcepoint-defined category.
- The ID/name belongs to a custom category defined via the TRITON Manager.
-
Failed attempts to create a new category might be caused by:
- The name provided is associated with another category.
-
Recurring error ‘Another transaction is in process …’:
This error might rise when running a playbook with parallel tasks assigned to the integration commands.
This error is caused by the Forcepoint data enforcement protocol. Any request to update/add/delete a category cannot run in parallel to another request of this type.
If this error arises, try to avoid assigning the following commands to parallel tasks:- ‘fp-add-category’
- ‘fp-add-addresses-to-category’
- ‘fp-delete-addresses-from-category’
- ‘fp-delete-category’
Configuration parameters
url— Server URL (e.g. https://192.168.0.1) (required)credentials— Credentials (required)proxy— Use system proxy settingsinsecure— Trust any certificate (not secure)versionCheck— Forcepoint version 8.5.5 or later
Commands (6)
-
fp-add-address-to-categoryAdd URLs and IP addresses to an API-managed category. Refer to category either by it's ID or by it's name.
-
fp-add-categoryCreate custom category.
-
fp-delete-address-from-categoryRemove URLs, IP addresses, and ranges from a specific API-managed category. Refer to category either by it's ID or by it's name.
-
fp-delete-categoriesDelete API-managed categories. Refer to categories you wish to delete either by ID list or by name list. It is possible to pass both lists, when each list refers to different categories.
-
fp-get-category-detailesGet a list of URLs and IP addresses in an API-managed category.
-
fp-list-categoriesRetrieve a list of all the categories, or only API-managed categories.
var SERVER_URL = params.url; if (SERVER_URL.slice(-1) === '/') { SERVER_URL = SERVER_URL.slice(0,-1); } SERVER_URL += ':15873/api/web/v1/'; var USER_NAME = params.credentials.identifier; var PASSWORD = params.credentials.password; var VER_85 = params.versionCheck function sendRequest(url, method, body, transactionID) { var req = { Method: method, Headers: { 'Content-Type': ['application/json'], 'Accept': ['application/json'] }, Username: USER_NAME, Password: PASSWORD }; var reqBody = {}; if (body) { reqBody = body; } if (transactionID) { reqBody['Transaction ID'] = transactionID; } // An empty body can cause the API to error out. Only send it on non-GETs. // If future GET Requests need a body, this logic will need to be updated. if (method !== 'GET'){ req.Body = JSON.stringify(reqBody); } var res = http(url, req, params.insecure, params.proxy); return res; } function transactionFlowRequest(url, method, body) { var transactionid = startTransaction(); var res = {}; try { res = sendRequest(url, method, body, transactionid); } catch (e) { throw e; } finally { //if request failes - commit the transaction anyway, to allow the creation a of new transaction for the next command. commitTransaction(transactionid); } var tries = 0; var status = {}; //check the systen status, wait until transaction build status id 'Done'. //The database is only updated after the build is done. //Will prevent the need to add 'wait' between playbook tasks do { wait(1); status = getTransactionStatus(); tries++; } while (status['Build Status'] !== 'Done' && tries < 10); if (tries > 10) { throw 'Possible transaction failure - transaction status: ' + status.Status.toString(); } return res; } function startTransaction() { var url = SERVER_URL + 'categories/start'; var tries = 0; var res = {}; // Start a new transacrion - 3 tries for recurring 409 error. //Error 409 - another transaction is in progress (Only one Policy API Server in a global TRITON AP-WEB deployment can start a transaction at a time). do { wait(1); res = sendRequest(url, 'POST'); tries++; } while(res.StatusCode === 409 && tries < 10); if (res.StatusCode !== 200) { if (res.StatusCode === 409) { throw 'Another transaction is in process. Please wait for a few seconds before sending another request.'; } throw 'error - request failed. error number ' + res.StatusCode; } var resBody = JSON.parse(res.Body); var tid = resBody['Transaction ID']; return tid; } function commitTransaction(transactionID) { var url = SERVER_URL + 'categories/commit?transactionid=' + transactionID; var res = sendRequest(url, 'POST'); if (res.StatusCode !== 200) { throw 'error - transaction commit failed. error ' + res.Status; } } function getTransactionStatus() { var url = SERVER_URL + 'categories/status'; var res = sendRequest(url, 'GET'); if (res.StatusCode !== 200) { throw 'Error- failed to get transaction status. possible transaction failure'; } try { var resBody = JSON.parse(res.Body); return resBody; } catch (err) { throw "error - unexpected response. Couldn't parse response body"; } } function parseResponse(response) { var body = JSON.parse(response.Body); if (response.StatusCode === 200) { return body; } else { throwErrDetails(body, response.StatusCode); } } function throwErrDetails(errResponse, StatusCode) { var erMessage = ''; var errArray = errResponse.Error ? errResponse.Error : []; for (var i = 0; i < errArray.length; i++) { erMessage = erMessage + errArray[i] + '\n'; } throw 'Request failed with status ' + StatusCode + '. ' + erMessage; } function addCategory(){ var res = addCategoryRequest( args.categoryName, args.categoryDescription, parseInt(args.parent)); var title = 'Forcepoint Add Category'; var content = res.Categories; var data = [ { to: 'CategoryName', from: 'Category Name' }]; var context = mapObjFunction(data) (content); var entry = { Type: entryTypes.note, Contents: content, ContentsFormat: formats.json, ReadableContentsFormat: formats.markdown, HumanReadable: tableToMarkdown(title, context), EntryContext: {} }; entry.EntryContext['Forcepoint.AddCategory(val.CategoryName==obj.CategoryName)'] = context; return entry; } function addCategoryRequest(categoryName, categoryDescription, parent) { var url = SERVER_URL + 'categories'; var body = { Categories: [ { 'Category Name': categoryName, 'Category Description': categoryDescription, 'Parent': parent } ] }; var res = transactionFlowRequest(url, 'POST', body); return parseResponse(res); } function listCategories(){ var listAll = (args.allCategories === 'true'); var res = listCategoriesRequest(listAll); var title = 'Forcepoint List Categories'; var categories = parseCategoryList(res.Categories); var data = [ {to: 'CategoryName', from: 'Category Name'}, {to: 'CategoryID', from: 'Category ID'}, {to: 'CategoryDescription', from: 'Category Description'}, {to: 'CategoryOwner', from: 'Category Owner'}, {to: 'CategoryParent', from: 'CategoryParent'} ]; var context = mapObjFunction(data)(categories); var entry = { Type: entryTypes.note, Contents: res, ContentsFormat: formats.json, ReadableContentsFormat: formats.markdown, HumanReadable: tableToMarkdown(title, context), EntryContext: {} }; entry.EntryContext['Forcepoint.ListCategories(val.CategoryID==obj.CategoryID)'] = createContext(context); return entry; } function parseCategoryList(categories){ var categoryArr = []; categories.forEach(function(cat) { var children = cat.Children; cat.CategoryParent = ''; categoryArr.push(cat); if (children) { for (var i = 0; i < children.length; i++) { var child = children[i]; child.CategoryParent = cat['Category Name']; categoryArr.push(child); } } }); return categoryArr; } function listCategoriesRequest(listAll) { var url = listAll ? SERVER_URL + 'categories/all' : SERVER_URL + 'categories'; var res = sendRequest(url, 'GET'); return parseResponse(res); } function categoryDetails(){ if (!args.categoryName && !args.categoryId) { throw "Please provide either the category name or it\' ID." } var res = args.categoryId ? categoryDetailsByIDRequest(args.categoryId) : categoryDetailsByNameRequest(args.categoryName); var title = 'Forcepoint Category Details'; var data = [ {to: 'CategoryName', from: 'Category Name'}, {to: 'CategoryID', from: 'Category ID'}, {to: 'URLs', from: 'URLs'}, {to: 'IPs', from: 'IPs'} ]; var context = mapObjFunction(data) (res); var entry = { Type: entryTypes.note, Contents: res, ContentsFormat: formats.json, ReadableContentsFormat: formats.markdown, HumanReadable: tableToMarkdown(title, context), EntryContext: {} }; entry.EntryContext['Forcepoint.CategoryDetails(val.CategoryID==obj.CategoryID)'] = createContext(context); return entry; } function categoryDetailsByNameRequest(name) { url = SERVER_URL + 'categories/urls?catname=' + name; var res = sendRequest(url, 'GET'); return parseResponse(res); } function categoryDetailsByIDRequest(id) { url = SERVER_URL + 'categories/urls?catid=' + id; var res = sendRequest(url, 'GET'); return parseResponse(res); } function addAddress() { if (!args.ips && !args.urls) { throw 'Please pass an ip list, url list, or both.' } if (!args.categoryID && !args.categoryName) { throw "Please provide either the category name or it's ID."; } var urls = args.urls ? args.urls.split(',') : undefined; var ips = args.ips ? args.ips.split(',') : undefined; var res = args.categoryID ? editAddressRequest(urls, ips, parseInt(args.categoryID)) : editAddressRequest(urls, ips, undefined, args.categoryName); var title = 'Forcepoint Category Details'; var data = [ {to: 'CategoryID', from: 'Category ID'}, {to: 'Totals.AddedURLs', from: 'Totals.Added URLs'}, {to: 'Totals.AddedIPs', from: 'Totals.Added IPs'} ]; var context = mapObjFunction(data) (res); var entry = { Type: entryTypes.note, Contents: res, ContentsFormat: formats.json, ReadableContentsFormat: formats.markdown, HumanReadable: tableToMarkdown(title, context), EntryContext: {} }; entry.EntryContext['Forcepoint.AddAddressToCategory(val.CategoryID==obj.CategoryID)'] = createContext(context); return entry; } function editAddressRequest(urls, ips, id, name, del){ var url = SERVER_URL + 'categories/urls'; var body = {}; if (id) { body['Category ID'] = id; } else if(name){ body['Category Name'] = name; } if (urls) { body.URLs = urls; } if (ips) { body.IPs = ips; } var method = del ? 'DELETE' : 'POST'; var res = transactionFlowRequest(url, method, body); return parseResponse(res); } function deleteAddressRequest(urls, ips, id, name){ var url = SERVER_URL + 'categories/delete/urls'; var body = {}; if (id) { body['Category ID'] = id; } else if(name){ body['Category Name'] = name; } if (urls) { body.URLs = urls; } if (ips) { body.IPs = ips; } var res = transactionFlowRequest(url, 'POST', body); return parseResponse(res); } function deleteAddress() { if (!args.ips && !args.urls) { throw 'Please pass an ip list, url list, or both.' } if (!args.categoryID && !args.categoryName) { throw "Please provide either the category name or it's ID."; } var urls = args.urls ? args.urls.split(',') : undefined; var ips = args.ips ? args.ips.split(',') : undefined; if (VER_85){ var res = args.categoryID ? deleteAddressRequest(urls, ips, parseInt(args.categoryID), undefined) : deleteAddressRequest(urls, ips, undefined, args.categoryName); } else { var res = args.categoryID ? editAddressRequest(urls, ips, parseInt(args.categoryID), undefined, true) : editAddressRequest(urls, ips, undefined, args.categoryName, true); } var title = 'Forcepoint Delete Address From Category'; var data = [ {to: 'CategoryID', from: 'Category ID'}, {to: 'Totals.DeletedURLs', from: 'Totals.Deleted URLs'}, {to: 'Totals.DeletedIPs', from: 'Totals.Deleted IPs'} ]; var context = mapObjFunction(data)(res); var entry = { Type: entryTypes.note, Contents: res, ContentsFormat: formats.json, ReadableContentsFormat: formats.markdown, HumanReadable: tableToMarkdown(title, context), EntryContext: {} }; entry.EntryContext['Forcepoint.DeleteAddressesFromCategory'] = createContext(context); return entry; } function deleteCategory() { if (!args.categoryIDs && !args.categoryNames) { throw 'Please provied IDs or/and Names of the categories you wish to delete.'; } //save the categories details before deleting them var deleted = []; if (args.categoryIDs) { var ids = []; if (Array.isArray(args.categoryIDs)) { ids = args.categoryIDs; } else if (typeof args.categoryIDs === 'string') { ids = args.categoryIDs.split(','); ids.forEach(function(element, index, arr) { arr[index] = parseInt(element); }); } else if (!isNaN(args.categoryIDs)) { ids.push(args.categoryIDs); } ids.forEach(function(element) { //Get details for each category. If the request fails, it means wrong category id/name was provided. //category DELETE request with wrong id/name will not cause an error. No need to end the procedure. Valid ids/names will still be deleted. try { var catDetails = categoryDetailsByIDRequest(element); deleted.push(catDetails); } catch(e) {} }); deleteCategoryRequest(ids); } if(args.categoryNames) { var names = []; if (Array.isArray(args.categoryNames)){ names = args.categoryNames; } else { names = args.categoryNames.split(','); } //Get details for each category. If the request fails, it means wrong category id/name was provided. //category DELETE request with wrong id/name will not cause an error. No need to end the procedure. Valid ids/names will still be deleted. try { names.forEach(function(element) { var catDetails = categoryDetailsByNameRequest(element); deleted.push(catDetails); }); } catch(e) {}; deleteCategoryRequest(undefined, names); } var data = [ {to: 'CategoryName', from: 'Category Name'}, {to: 'CategoryID', from: 'Category ID'}, {to: 'URLs', from: 'URLs'}, {to: 'IPs', from: 'IPs'} ]; var context = mapObjFunction(data) (deleted); var entry = { Type: entryTypes.note, ReadableContentsFormat: formats.text, HumanReadable: "Categories were deleted successfully", Contents: deleted, ContentsFormat: formats.json, EntryContext: {} }; entry.EntryContext['Forcepoint.DeletedCategories'] = createContext(context); return entry; } function deleteCategoryRequest(ids, names) { if (VER_85){ var url = SERVER_URL + 'categories/delete'; var method = 'POST'; logDebug('Using new post method to delete category'); } else { var url = SERVER_URL + 'categories'; method = 'DELETE'; } var body = {}; if (ids) { body['Category IDs'] = ids; } else if (names) { body['Category Names'] = names; } var res = transactionFlowRequest(url, method, body); if (res.StatusCode !== 200) { var errResponse = JSON.stringify(res.Body); throwErrDetails(errResponse, res.StatusCode); } } function test() { var tid = startTransaction(); commitTransaction(tid); return 'ok'; } switch(command) { case 'test-module': return test(); case 'fp-add-category': return addCategory(); case 'fp-list-categories': return listCategories(); case 'fp-get-category-detailes': return categoryDetails(); case 'fp-add-address-to-category': return addAddress(); case 'fp-delete-address-from-category': return deleteAddress(); case 'fp-delete-categories': return deleteCategory(); }