From f0b77b4ce4c0099257b3c2912cbe7365dc151218 Mon Sep 17 00:00:00 2001 From: Habib Nuhu Date: Thu, 11 Dec 2025 00:12:21 +0100 Subject: [PATCH 1/3] Add Tenable custom integration documentation - Add comprehensive Tenable.io integration guide using Ocean Custom Integration - Include prerequisites for API key creation and Basic Authentication setup - Add installation instructions for both Helm and Docker - Document supported resources: Assets, Vulnerabilities, Scans, Findings - Include blueprints for Tenable Asset, Vulnerability, and Scan entities - Add resource mapping examples for Assets, Vulnerabilities, and Scans - Include troubleshooting section for common issues (authentication, rate limiting, pagination) - Add Tenable to plug-and-play integrations catalog in Code Quality & Security category - Add category file with custom-integration tag --- .../tenable/_category_.json | 6 + .../code-quality-security/tenable/tenable.md | 587 ++++++++++++++++++ .../IntegrationsTable/integrations-data.js | 7 + 3 files changed, 600 insertions(+) create mode 100644 docs/build-your-software-catalog/sync-data-to-catalog/code-quality-security/tenable/_category_.json create mode 100644 docs/build-your-software-catalog/sync-data-to-catalog/code-quality-security/tenable/tenable.md diff --git a/docs/build-your-software-catalog/sync-data-to-catalog/code-quality-security/tenable/_category_.json b/docs/build-your-software-catalog/sync-data-to-catalog/code-quality-security/tenable/_category_.json new file mode 100644 index 0000000000..1855b2a856 --- /dev/null +++ b/docs/build-your-software-catalog/sync-data-to-catalog/code-quality-security/tenable/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Tenable", + "position": 7, + "className": "custom-integration" +} + diff --git a/docs/build-your-software-catalog/sync-data-to-catalog/code-quality-security/tenable/tenable.md b/docs/build-your-software-catalog/sync-data-to-catalog/code-quality-security/tenable/tenable.md new file mode 100644 index 0000000000..0897762d28 --- /dev/null +++ b/docs/build-your-software-catalog/sync-data-to-catalog/code-quality-security/tenable/tenable.md @@ -0,0 +1,587 @@ +import Tabs from "@theme/Tabs" +import TabItem from "@theme/TabItem" +import Prerequisites from "/docs/build-your-software-catalog/sync-data-to-catalog/templates/_ocean_helm_prerequisites_block.mdx" +import AdvancedConfig from '/docs/generalTemplates/_ocean_advanced_configuration_note.md' +import PortApiRegionTip from "/docs/generalTemplates/_port_region_parameter_explanation_template.md" +import CustomOceanIntegration from "/docs/build-your-software-catalog/sync-data-to-catalog/templates/_custom_ocean_integration.mdx" + +# Tenable + + + +Port's Tenable integration allows you to ingest Tenable.io vulnerability management data into your software catalog using the [Ocean Custom Integration](/build-your-software-catalog/custom-integration/ocean-custom-integration/overview) framework. After installing this integration, you can visualize vulnerabilities, assets, scans, and findings from your Tenable.io instance. + +## Supported resources + +The Tenable integration can ingest the following resources into Port. It is possible to reference any field that appears in the API responses in the mapping configuration. For detailed API documentation, see the [Tenable.io API documentation](https://developer.tenable.com/reference/navigate). + +- **Assets** - Asset information from [`/assets`](https://developer.tenable.com/reference/io-v2-assets-list). +- **Vulnerabilities** - Vulnerability findings from [`/vulnerabilities`](https://developer.tenable.com/reference/io-v2-vulnerabilities-list). +- **Scans** - Scan configurations and results from [`/scans`](https://developer.tenable.com/reference/io-v2-scans-list). +- **Findings** - Vulnerability findings with asset context from [`/workbenches/vulnerabilities`](https://developer.tenable.com/reference/io-v2-workbenches-vulnerabilities-list). + +## Prerequisites + +To use this integration, you need: + +- A Tenable.io account with API access. +- An API key pair (Access Key and Secret Key) for authentication. +- Appropriate permissions to access assets, vulnerabilities, and scans. + +**To create API keys in Tenable.io:** + +1. Log in to your [Tenable.io account](https://cloud.tenable.com). +2. Navigate to **Settings** > **API Keys**. +3. Click **Generate** to create a new API key pair. +4. **Copy both the Access Key and Secret Key immediately** - the Secret Key will not be shown again. +5. Store the keys securely. + +:::warning API key security +Store your API keys securely and never share them. The keys provide access to your Tenable.io data. +::: + +**API key format:** + +Tenable.io uses API key authentication where: +- **Access Key**: Used as the username in HTTP Basic Authentication +- **Secret Key**: Used as the password in HTTP Basic Authentication + +The integration will use Basic Authentication with these credentials. + +## Installation + +Choose one of the following installation methods to deploy the Ocean Custom Integration: + + + + + +

Prerequisites

+ + + +

Installation

+ +Add Port's Helm repo and install the Ocean Custom Integration: + +:::note Replace placeholders +Remember to replace the placeholders for `YOUR_PORT_CLIENT_ID`, `YOUR_PORT_CLIENT_SECRET`, `YOUR_TENABLE_ACCESS_KEY`, and `YOUR_TENABLE_SECRET_KEY`. +::: + +```bash showLineNumbers +helm repo add --force-update port-labs https://port-labs.github.io/helm-charts +helm upgrade --install my-ocean-tenable-integration port-labs/port-ocean \ + --set port.clientId="YOUR_PORT_CLIENT_ID" \ + --set port.clientSecret="YOUR_PORT_CLIENT_SECRET" \ + --set port.baseUrl="https://api.getport.io" \ + --set initializePortResources=true \ + --set scheduledResyncInterval=120 \ + --set integration.identifier="tenable-integration" \ + --set integration.type="custom" \ + --set integration.eventListener.type="POLLING" \ + --set integration.config.baseUrl="https://cloud.tenable.com" \ + --set integration.config.authType="basic" \ + --set integration.config.username="YOUR_TENABLE_ACCESS_KEY" \ + --set integration.config.password="YOUR_TENABLE_SECRET_KEY" +``` + + + +

Configuration parameters

+ +This table summarizes the available parameters for the installation. + +| Parameter | Description | Example | Required | +| --- | --- | --- | --- | +| `port.clientId` | Your Port [client id](https://docs.port.io/build-your-software-catalog/custom-integration/api/#find-your-port-credentials). | | ✅ | +| `port.clientSecret` | Your Port [client secret](https://docs.port.io/build-your-software-catalog/custom-integration/api/#find-your-port-credentials). | | ✅ | +| `port.baseUrl` | Your Port API URL (`https://api.getport.io` for EU, `https://api.us.getport.io` for US). | | ✅ | +| `integration.config.baseUrl` | Base URL of the Tenable.io API. | https://cloud.tenable.com | ✅ | +| `integration.config.authType` | Authentication type for the API (use `basic` for Tenable.io). | basic | ✅ | +| `integration.config.username` | Your Tenable.io Access Key (used as username in Basic Auth). | | ✅ | +| `integration.config.password` | Your Tenable.io Secret Key (used as password in Basic Auth). | | ✅ | +| `integration.config.paginationType` | How the API handles pagination (offset, page, cursor, or none). | offset | ❌ | +| `integration.eventListener.type` | Event listener type. See [event listeners](https://ocean.getport.io/framework/features/event-listener). | POLLING | ✅ | +| `integration.type` | Integration type (must be `custom`). | custom | ✅ | +| `integration.identifier` | Unique identifier for the integration instance. | tenable-integration | ✅ | +| `scheduledResyncInterval` | Minutes between scheduled syncs. When omitted, the event listener interval is used. | 120 | ❌ | +| `initializePortResources` | When true, creates default blueprints and mappings on first run. | true | ❌ | +| `sendRawDataExamples` | Sends sample payloads from the API to Port for easier mapping. | true | ❌ | + +
+ + + +
+ + + +To run the integration using Docker for a one-time sync: + +:::note Replace placeholders +Remember to replace the placeholders for `YOUR_PORT_CLIENT_ID`, `YOUR_PORT_CLIENT_SECRET`, `YOUR_TENABLE_ACCESS_KEY`, and `YOUR_TENABLE_SECRET_KEY`. +::: + +```bash showLineNumbers +docker run -i --rm --platform=linux/amd64 \ + -e OCEAN__EVENT_LISTENER='{"type":"ONCE"}' \ + -e OCEAN__INITIALIZE_PORT_RESOURCES=true \ + -e OCEAN__SEND_RAW_DATA_EXAMPLES=true \ + -e OCEAN__INTEGRATION__CONFIG__BASE_URL="https://cloud.tenable.com" \ + -e OCEAN__INTEGRATION__CONFIG__AUTH_TYPE="basic" \ + -e OCEAN__INTEGRATION__CONFIG__USERNAME="YOUR_TENABLE_ACCESS_KEY" \ + -e OCEAN__INTEGRATION__CONFIG__PASSWORD="YOUR_TENABLE_SECRET_KEY" \ + -e OCEAN__PORT__CLIENT_ID="YOUR_PORT_CLIENT_ID" \ + -e OCEAN__PORT__CLIENT_SECRET="YOUR_PORT_CLIENT_SECRET" \ + -e OCEAN__PORT__BASE_URL="https://api.getport.io" \ + ghcr.io/port-labs/port-ocean-custom:latest +``` + + + + + + + +
+ +## Set up data model + +Before the integration can sync data, you need to create the required blueprints in Port. These blueprints define the data model for your Tenable resources. + +**To create the blueprints:** + +1. Go to your [Builder page](https://app.getport.io/settings/data-model). + +2. Click on the `+ Blueprint` button. + +3. Copy and paste each blueprint JSON from the sections below. + +
+ Tenable Asset Blueprint (Click to expand) + + Asset information: + + ```json showLineNumbers + { + "identifier": "tenable-asset", + "title": "Tenable Asset", + "icon": "Security", + "schema": { + "properties": { + "hostname": { + "title": "Hostname", + "type": "string" + }, + "ipAddress": { + "title": "IP Address", + "type": "string" + }, + "fqdn": { + "title": "FQDN", + "type": "string" + }, + "operatingSystem": { + "title": "Operating System", + "type": "array", + "items": { + "type": "string" + } + }, + "macAddress": { + "title": "MAC Address", + "type": "string" + }, + "agentUuid": { + "title": "Agent UUID", + "type": "string" + }, + "lastSeen": { + "title": "Last Seen", + "type": "string", + "format": "date-time" + }, + "firstSeen": { + "title": "First Seen", + "type": "string", + "format": "date-time" + }, + "hasAgent": { + "title": "Has Agent", + "type": "boolean" + }, + "vulnerabilityCount": { + "title": "Vulnerability Count", + "type": "number" + } + }, + "required": [] + }, + "mirrorProperties": {}, + "calculationProperties": {}, + "aggregationProperties": {}, + "relations": {} + } + ``` + +
+ +
+ Tenable Vulnerability Blueprint (Click to expand) + + Vulnerability findings: + + ```json showLineNumbers + { + "identifier": "tenable-vulnerability", + "title": "Tenable Vulnerability", + "icon": "Security", + "schema": { + "properties": { + "pluginId": { + "title": "Plugin ID", + "type": "string" + }, + "pluginName": { + "title": "Plugin Name", + "type": "string" + }, + "severity": { + "title": "Severity", + "type": "string" + }, + "state": { + "title": "State", + "type": "string" + }, + "description": { + "title": "Description", + "type": "string" + }, + "solution": { + "title": "Solution", + "type": "string" + }, + "cvssScore": { + "title": "CVSS Score", + "type": "number" + }, + "cvssV3Score": { + "title": "CVSS v3 Score", + "type": "number" + }, + "cve": { + "title": "CVE", + "type": "array", + "items": { + "type": "string" + } + }, + "assetCount": { + "title": "Asset Count", + "type": "number" + }, + "firstSeen": { + "title": "First Seen", + "type": "string", + "format": "date-time" + }, + "lastSeen": { + "title": "Last Seen", + "type": "string", + "format": "date-time" + } + }, + "required": [] + }, + "mirrorProperties": {}, + "calculationProperties": {}, + "aggregationProperties": {}, + "relations": {} + } + ``` + +
+ +
+ Tenable Scan Blueprint (Click to expand) + + Scan configurations and results: + + ```json showLineNumbers + { + "identifier": "tenable-scan", + "title": "Tenable Scan", + "icon": "Security", + "schema": { + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "title": "Description", + "type": "string" + }, + "status": { + "title": "Status", + "type": "string" + }, + "scanType": { + "title": "Scan Type", + "type": "string" + }, + "scheduleEnabled": { + "title": "Schedule Enabled", + "type": "boolean" + }, + "timezone": { + "title": "Timezone", + "type": "string" + }, + "startTime": { + "title": "Start Time", + "type": "string" + }, + "endTime": { + "title": "End Time", + "type": "string", + "format": "date-time" + }, + "createdAt": { + "title": "Created At", + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "title": "Updated At", + "type": "string", + "format": "date-time" + }, + "vulnerabilityCount": { + "title": "Vulnerability Count", + "type": "number" + } + }, + "required": [] + }, + "mirrorProperties": {}, + "calculationProperties": {}, + "aggregationProperties": {}, + "relations": {} + } + ``` + +
+ +## Configuration + +After installation, define which endpoints to sync in your integration configuration. Each resource maps an API endpoint to Port entities using [JQ expressions](https://stedolan.github.io/jq/manual/) to transform the data. + +**Key mapping components:** +- **`kind`**: The API endpoint path (combined with your base URL). +- **`selector.query`**: JQ filter to include/exclude entities (use `'true'` to sync all). +- **`selector.data_path`**: JQ expression pointing to the array of items in the response. +- **`port.entity.mappings`**: How to map API fields to Port entity properties. + +For more details on how the Ocean Custom Integration works, see the [How it works](https://docs.port.io/build-your-software-catalog/custom-integration/ocean-custom-integration/overview#how-it-works) section in the custom integration overview. + +**Tenable.io API response format:** + +Tenable.io API responses typically follow this structure: + +```json showLineNumbers +{ + "assets": [ + { + "id": "asset-uuid", + "hostname": "example.com", + "ipv4": ["192.168.1.1"], + "fqdn": ["example.com"], + "operating_system": ["Linux"], + ... + } + ], + "pagination": { + "total": 100, + "limit": 50, + "offset": 0, + "sort": [{"name": "updated_at", "order": "desc"}] + } +} +``` + +The actual data array is typically in a property like `.assets`, `.vulnerabilities`, or `.scans`, and pagination information is in `.pagination`. + +**To configure the mappings:** + +1. Go to your [data sources page](https://app.getport.io/settings/data-sources). + +2. Find your Tenable integration in the list. + +3. Click on the integration to open the mapping editor. + +4. Add the resource mapping configurations below. + +
+ Assets mapping (Click to expand) + + ```yaml showLineNumbers + resources: + - kind: /assets + selector: + query: 'true' + data_path: '.assets' + query_params: + limit: "50" + offset: "0" + port: + entity: + mappings: + identifier: .id + title: .hostname // .ipv4[0] // .id + blueprint: '"tenable-asset"' + properties: + hostname: .hostname + ipAddress: .ipv4[0] // "" + fqdn: .fqdn[0] // "" + operatingSystem: .operating_system + macAddress: .mac_address + agentUuid: .agent_uuid + lastSeen: .last_seen + firstSeen: .first_seen + hasAgent: .has_agent + vulnerabilityCount: .vulnerability_count + ``` + + :::info Tenable.io pagination + Tenable.io uses offset-based pagination. Use `limit` and `offset` query parameters to paginate through results. The integration will automatically handle pagination if `paginationType` is set to `offset`. + ::: + +
+ +
+ Vulnerabilities mapping (Click to expand) + + ```yaml showLineNumbers + resources: + - kind: /vulnerabilities + selector: + query: 'true' + data_path: '.vulnerabilities' + query_params: + limit: "50" + offset: "0" + port: + entity: + mappings: + identifier: .plugin_id + title: .plugin_name + blueprint: '"tenable-vulnerability"' + properties: + pluginId: .plugin_id + pluginName: .plugin_name + severity: .severity + state: .state + description: .description + solution: .solution + cvssScore: .cvss_score + cvssV3Score: .cvss_v3_score + cve: .cve + assetCount: .asset_count + firstSeen: .first_seen + lastSeen: .last_seen + ``` + +
+ +
+ Scans mapping (Click to expand) + + ```yaml showLineNumbers + resources: + - kind: /scans + selector: + query: 'true' + data_path: '.scans' + query_params: + limit: "50" + offset: "0" + port: + entity: + mappings: + identifier: .id + title: .name + blueprint: '"tenable-scan"' + properties: + name: .name + description: .description + status: .status + scanType: .type + scheduleEnabled: .schedule.enabled + timezone: .schedule.timezone + startTime: .starttime + endTime: .endtime + createdAt: .creation_date + updatedAt: .last_modification_date + vulnerabilityCount: .vulnerabilities.count + ``` + +
+ +5. Click `Save` to save the mapping. + +## Customization + +If you want to customize your setup or test different API endpoints before committing to a configuration, use the [interactive builder](/build-your-software-catalog/custom-integration/ocean-custom-integration/build-your-integration). + +**The interactive builder helps you:** +1. Test your Tenable.io API endpoints with live data. +2. Automatically detect the data structure and field types. +3. Generate blueprints and resource mappings tailored to your preferences. +4. Get installation commands with your configuration pre-filled. + +Simply provide your Tenable.io API details, and the builder will generate everything you need to install and create the integration in Port. + +## Troubleshooting + +### Authentication errors + +**Symptom:** 401 "Unauthorized" errors. + +**Solution:** +- Verify your Access Key and Secret Key are correct +- Ensure the API keys are active and not expired +- Check that your account has the necessary permissions to access the requested resources +- Verify you're using Basic Authentication with Access Key as username and Secret Key as password + +### Rate limiting errors + +**Symptom:** 429 "Too Many Requests" errors. + +**Solution:** +- Tenable.io has rate limits on API requests +- Reduce the `scheduledResyncInterval` to sync less frequently +- Use smaller `limit` values in query parameters to reduce request size +- Implement retry logic with exponential backoff if needed + +### Pagination issues + +**Symptom:** Only partial data is syncing. + +**Solution:** +- Ensure `paginationType` is set to `offset` for Tenable.io +- Verify `limit` and `offset` parameters are correctly configured in query_params +- Check that the integration is handling pagination correctly by reviewing logs +- Consider using smaller `limit` values if experiencing timeouts + +### API endpoint errors + +**Symptom:** 404 "Not Found" or 403 "Forbidden" errors. + +**Solution:** +- Verify the API endpoint paths are correct for your Tenable.io version +- Check that your API keys have permissions for the requested resources +- Ensure you're using the correct base URL (`https://cloud.tenable.com` for Tenable.io) +- Review the [Tenable.io API documentation](https://developer.tenable.com/reference/navigate) for the correct endpoint structure + diff --git a/src/components/IntegrationsTable/integrations-data.js b/src/components/IntegrationsTable/integrations-data.js index e4ce07fe9c..036786655c 100644 --- a/src/components/IntegrationsTable/integrations-data.js +++ b/src/components/IntegrationsTable/integrations-data.js @@ -206,6 +206,13 @@ export const integrations = [ darkIcon: '/img/guides/icons/dark/Armorcode.svg', docsUrl: '/build-your-software-catalog/sync-data-to-catalog/code-quality-security/armorcode', }, + { + name: 'Tenable', + category: 'security', + icon: '/img/guides/icons/Security.svg', + darkIcon: '/img/guides/icons/Security.svg', + docsUrl: '/build-your-software-catalog/sync-data-to-catalog/code-quality-security/tenable', + }, // Feature Management { name: 'Launchdarkly', From 43f5d14435388303ddf66ff69548e219b058a7b0 Mon Sep 17 00:00:00 2001 From: Habib Nuhu Date: Wed, 17 Dec 2025 07:46:40 +0100 Subject: [PATCH 2/3] Added Tenable icon --- src/components/IntegrationsTable/integrations-data.js | 4 ++-- static/img/guides/icons/Tenable.svg | 4 ++++ static/img/guides/icons/dark/Tenable.svg | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 static/img/guides/icons/Tenable.svg create mode 100644 static/img/guides/icons/dark/Tenable.svg diff --git a/src/components/IntegrationsTable/integrations-data.js b/src/components/IntegrationsTable/integrations-data.js index 036786655c..d83ec4f130 100644 --- a/src/components/IntegrationsTable/integrations-data.js +++ b/src/components/IntegrationsTable/integrations-data.js @@ -209,8 +209,8 @@ export const integrations = [ { name: 'Tenable', category: 'security', - icon: '/img/guides/icons/Security.svg', - darkIcon: '/img/guides/icons/Security.svg', + icon: '/img/guides/icons/Tenable.svg', + darkIcon: '/img/guides/icons/dark/Tenable.svg', docsUrl: '/build-your-software-catalog/sync-data-to-catalog/code-quality-security/tenable', }, // Feature Management diff --git a/static/img/guides/icons/Tenable.svg b/static/img/guides/icons/Tenable.svg new file mode 100644 index 0000000000..e6f506c7d3 --- /dev/null +++ b/static/img/guides/icons/Tenable.svg @@ -0,0 +1,4 @@ + + + + diff --git a/static/img/guides/icons/dark/Tenable.svg b/static/img/guides/icons/dark/Tenable.svg new file mode 100644 index 0000000000..ac8ccf9efe --- /dev/null +++ b/static/img/guides/icons/dark/Tenable.svg @@ -0,0 +1,4 @@ + + + + From f70012cb17915f65a0e0cf77bd02a6f2c94e7c08 Mon Sep 17 00:00:00 2001 From: Habib Nuhu Date: Wed, 17 Dec 2025 09:40:59 +0100 Subject: [PATCH 3/3] Fix Tenable integration: use workbenches/vulnerabilities endpoint with cursor pagination - Replace /vulnerabilities with /workbenches/vulnerabilities for per-asset findings - Update Finding blueprint with asset context fields - Fix pagination: cursor for workbenches, offset for assets/scans - Fix scan timestamps to number type (epoch) - Fix MAC address to handle array format - Update composite identifier for findings uniqueness --- .../code-quality-security/tenable/tenable.md | 186 +++++++----------- 1 file changed, 73 insertions(+), 113 deletions(-) diff --git a/docs/build-your-software-catalog/sync-data-to-catalog/code-quality-security/tenable/tenable.md b/docs/build-your-software-catalog/sync-data-to-catalog/code-quality-security/tenable/tenable.md index 0897762d28..322d100023 100644 --- a/docs/build-your-software-catalog/sync-data-to-catalog/code-quality-security/tenable/tenable.md +++ b/docs/build-your-software-catalog/sync-data-to-catalog/code-quality-security/tenable/tenable.md @@ -16,9 +16,8 @@ Port's Tenable integration allows you to ingest Tenable.io vulnerability managem The Tenable integration can ingest the following resources into Port. It is possible to reference any field that appears in the API responses in the mapping configuration. For detailed API documentation, see the [Tenable.io API documentation](https://developer.tenable.com/reference/navigate). - **Assets** - Asset information from [`/assets`](https://developer.tenable.com/reference/io-v2-assets-list). -- **Vulnerabilities** - Vulnerability findings from [`/vulnerabilities`](https://developer.tenable.com/reference/io-v2-vulnerabilities-list). +- **Findings** - Vulnerability findings with asset context from [`/workbenches/vulnerabilities`](https://developer.tenable.com/reference/io-v2-workbenches-vulnerabilities-list). This endpoint provides per-asset vulnerability findings with fields like `first_seen`, `last_seen`, `state`, and `severity`. - **Scans** - Scan configurations and results from [`/scans`](https://developer.tenable.com/reference/io-v2-scans-list). -- **Findings** - Vulnerability findings with asset context from [`/workbenches/vulnerabilities`](https://developer.tenable.com/reference/io-v2-workbenches-vulnerabilities-list). ## Prerequisites @@ -74,7 +73,7 @@ helm upgrade --install my-ocean-tenable-integration port-labs/port-ocean \ --set port.clientId="YOUR_PORT_CLIENT_ID" \ --set port.clientSecret="YOUR_PORT_CLIENT_SECRET" \ --set port.baseUrl="https://api.getport.io" \ - --set initializePortResources=true \ + --set initializePortResources=false \ --set scheduledResyncInterval=120 \ --set integration.identifier="tenable-integration" \ --set integration.type="custom" \ @@ -100,7 +99,7 @@ This table summarizes the available parameters for the installation. | `integration.config.authType` | Authentication type for the API (use `basic` for Tenable.io). | basic | ✅ | | `integration.config.username` | Your Tenable.io Access Key (used as username in Basic Auth). | | ✅ | | `integration.config.password` | Your Tenable.io Secret Key (used as password in Basic Auth). | | ✅ | -| `integration.config.paginationType` | How the API handles pagination (offset, page, cursor, or none). | offset | ❌ | +| `integration.config.paginationType` | How the API handles pagination. Use `offset` for `/assets` and `/scans`, `cursor` for `/workbenches/vulnerabilities`. | offset | ❌ | | `integration.eventListener.type` | Event listener type. See [event listeners](https://ocean.getport.io/framework/features/event-listener). | POLLING | ✅ | | `integration.type` | Integration type (must be `custom`). | custom | ✅ | | `integration.identifier` | Unique identifier for the integration instance. | tenable-integration | ✅ | @@ -190,7 +189,10 @@ Before the integration can sync data, you need to create the required blueprints }, "macAddress": { "title": "MAC Address", - "type": "string" + "type": "array", + "items": { + "type": "string" + } }, "agentUuid": { "title": "Agent UUID", @@ -227,14 +229,14 @@ Before the integration can sync data, you need to create the required blueprints
- Tenable Vulnerability Blueprint (Click to expand) + Tenable Finding Blueprint (Click to expand) - Vulnerability findings: + Vulnerability findings with asset context: ```json showLineNumbers { - "identifier": "tenable-vulnerability", - "title": "Tenable Vulnerability", + "identifier": "tenable-finding", + "title": "Tenable Finding", "icon": "Security", "schema": { "properties": { @@ -277,9 +279,13 @@ Before the integration can sync data, you need to create the required blueprints "type": "string" } }, - "assetCount": { - "title": "Asset Count", - "type": "number" + "assetId": { + "title": "Asset ID", + "type": "string" + }, + "assetHostname": { + "title": "Asset Hostname", + "type": "string" }, "firstSeen": { "title": "First Seen", @@ -341,25 +347,18 @@ Before the integration can sync data, you need to create the required blueprints }, "startTime": { "title": "Start Time", - "type": "string" + "type": "number" }, "endTime": { "title": "End Time", - "type": "string", - "format": "date-time" + "type": "number" }, "createdAt": { "title": "Created At", - "type": "string", - "format": "date-time" + "type": "number" }, "updatedAt": { "title": "Updated At", - "type": "string", - "format": "date-time" - }, - "vulnerabilityCount": { - "title": "Vulnerability Count", "type": "number" } }, @@ -388,30 +387,17 @@ For more details on how the Ocean Custom Integration works, see the [How it work **Tenable.io API response format:** -Tenable.io API responses typically follow this structure: +Tenable.io API responses vary by endpoint: -```json showLineNumbers -{ - "assets": [ - { - "id": "asset-uuid", - "hostname": "example.com", - "ipv4": ["192.168.1.1"], - "fqdn": ["example.com"], - "operating_system": ["Linux"], - ... - } - ], - "pagination": { - "total": 100, - "limit": 50, - "offset": 0, - "sort": [{"name": "updated_at", "order": "desc"}] - } -} -``` +- **`/assets`** - Returns data in `.assets` array with offset-based pagination in `.pagination` +- **`/workbenches/vulnerabilities`** - Returns data in `.vulnerabilities` array with cursor-based pagination +- **`/scans`** - Returns data in `.scans` array with offset-like pagination + +**Important notes:** -The actual data array is typically in a property like `.assets`, `.vulnerabilities`, or `.scans`, and pagination information is in `.pagination`. +- Timestamps in Tenable API responses are often returned as **epoch timestamps** (numbers), not ISO strings. You may need to convert them or store them as numbers. +- The `mac_address` field is typically an **array**, not a string. +- Pagination types differ by endpoint - use `offset` for `/assets`, `cursor` for `/workbenches/vulnerabilities`. **To configure the mappings:** @@ -446,7 +432,7 @@ The actual data array is typically in a property like `.assets`, `.vulnerabiliti ipAddress: .ipv4[0] // "" fqdn: .fqdn[0] // "" operatingSystem: .operating_system - macAddress: .mac_address + macAddress: (.mac_address | if type == "array" then . else [.] end) agentUuid: .agent_uuid lastSeen: .last_seen firstSeen: .first_seen @@ -454,45 +440,55 @@ The actual data array is typically in a property like `.assets`, `.vulnerabiliti vulnerabilityCount: .vulnerability_count ``` - :::info Tenable.io pagination - Tenable.io uses offset-based pagination. Use `limit` and `offset` query parameters to paginate through results. The integration will automatically handle pagination if `paginationType` is set to `offset`. + :::info Pagination for /assets + The `/assets` endpoint uses offset-based pagination. Set `paginationType` to `offset` in your integration configuration. Use `limit` and `offset` query parameters to paginate through results. :::
- Vulnerabilities mapping (Click to expand) + Findings mapping (Click to expand) + + Vulnerability findings with asset context from `/workbenches/vulnerabilities`: ```yaml showLineNumbers resources: - - kind: /vulnerabilities + - kind: /workbenches/vulnerabilities selector: query: 'true' data_path: '.vulnerabilities' query_params: limit: "50" - offset: "0" port: entity: mappings: - identifier: .plugin_id - title: .plugin_name - blueprint: '"tenable-vulnerability"' + identifier: '"\(.plugin.id)-\(.asset.uuid)"' + title: .plugin.name + blueprint: '"tenable-finding"' properties: - pluginId: .plugin_id - pluginName: .plugin_name - severity: .severity + pluginId: .plugin.id + pluginName: .plugin.name + severity: (.severity.name // .severity) state: .state - description: .description - solution: .solution - cvssScore: .cvss_score - cvssV3Score: .cvss_v3_score - cve: .cve - assetCount: .asset_count - firstSeen: .first_seen - lastSeen: .last_seen + description: .plugin.description + solution: .plugin.solution + cvssScore: (.plugin.cvss_base_score // .cvss_score) + cvssV3Score: (.plugin.cvss3_base_score // .cvss_v3_score) + cve: (.plugin.cve // []) + assetId: .asset.uuid + assetHostname: .asset.hostname + firstSeen: .first_found + lastSeen: .last_found ``` + :::warning Pagination for /workbenches/vulnerabilities + The `/workbenches/vulnerabilities` endpoint uses **cursor-based pagination**, not offset. Set `paginationType` to `cursor` in your integration configuration. The cursor path is typically `.pagination.cursor` or similar. Do not use `offset` query parameters with this endpoint. + ::: + + :::info Composite identifier + The identifier uses a composite of `plugin_id` and `asset.uuid` to ensure uniqueness, as the same plugin can appear on multiple assets. This prevents findings from overwriting each other. + ::: +
@@ -518,15 +514,22 @@ The actual data array is typically in a property like `.assets`, `.vulnerabiliti description: .description status: .status scanType: .type - scheduleEnabled: .schedule.enabled + scheduleEnabled: (.schedule.enabled // false) timezone: .schedule.timezone - startTime: .starttime - endTime: .endtime - createdAt: .creation_date - updatedAt: .last_modification_date - vulnerabilityCount: .vulnerabilities.count + startTime: (.starttime // null) + endTime: (.endtime // null) + createdAt: (.creation_date // null) + updatedAt: (.last_modification_date // null) ``` + :::caution Timestamp format + Tenable scan timestamps (`starttime`, `endtime`, `creation_date`, `last_modification_date`) are often returned as **epoch timestamps** (numbers), not ISO date strings. The blueprint stores them as numbers. If you need ISO format, you can convert them using JQ expressions or store them as strings. + ::: + + :::info Pagination for /scans + The `/scans` endpoint uses offset-like pagination. Set `paginationType` to `offset` in your integration configuration. + ::: +
5. Click `Save` to save the mapping. @@ -542,46 +545,3 @@ If you want to customize your setup or test different API endpoints before commi 4. Get installation commands with your configuration pre-filled. Simply provide your Tenable.io API details, and the builder will generate everything you need to install and create the integration in Port. - -## Troubleshooting - -### Authentication errors - -**Symptom:** 401 "Unauthorized" errors. - -**Solution:** -- Verify your Access Key and Secret Key are correct -- Ensure the API keys are active and not expired -- Check that your account has the necessary permissions to access the requested resources -- Verify you're using Basic Authentication with Access Key as username and Secret Key as password - -### Rate limiting errors - -**Symptom:** 429 "Too Many Requests" errors. - -**Solution:** -- Tenable.io has rate limits on API requests -- Reduce the `scheduledResyncInterval` to sync less frequently -- Use smaller `limit` values in query parameters to reduce request size -- Implement retry logic with exponential backoff if needed - -### Pagination issues - -**Symptom:** Only partial data is syncing. - -**Solution:** -- Ensure `paginationType` is set to `offset` for Tenable.io -- Verify `limit` and `offset` parameters are correctly configured in query_params -- Check that the integration is handling pagination correctly by reviewing logs -- Consider using smaller `limit` values if experiencing timeouts - -### API endpoint errors - -**Symptom:** 404 "Not Found" or 403 "Forbidden" errors. - -**Solution:** -- Verify the API endpoint paths are correct for your Tenable.io version -- Check that your API keys have permissions for the requested resources -- Ensure you're using the correct base URL (`https://cloud.tenable.com` for Tenable.io) -- Review the [Tenable.io API documentation](https://developer.tenable.com/reference/navigate) for the correct endpoint structure -