Build custom connectors with the SDK
The Surface Command Software Development Kit (SDK), surcom-sdk, provides tools to develop Surface Command data connectors for the Rapid7 Command Platform. To learn more about connectors, see Connectors. To learn more about Surface Command and Attack Surface Management, see Get started with Attack Surface Management.
Prerequisites
- Python version 3.11 or greater
- Using a virtual environment is recommended
- Docker fully configured and running
- Permissions to access Attack Surface Management capabilities and create an API key in the Command Platform
- Familiarity with the Attack Surface Management type system
- Optional:
- Integrated Developer Environment (IDE)
- Example: Microsoft Visual Studio Code
- 1Password
- Integrated Developer Environment (IDE)
Install and configure the SDK
Complete the following tasks to set up the SDK properly:
Task 1: Create an API Key
Generate a new user API key in the Command Platform and copy the key before closing the window. You can save the key as an API Credential in 1Password (recommended) so it can be accessed using a 1Password Secret Reference . Alternatively, you can save it as plain text (not recommended).
Task 2: Configure your local environment
-
Open a terminal.
-
Create a
developmentdirectory to house your SDK development files:cd ~ mkdir development cd development -
Clone the Surface Command Connectors repository :
git clone https://github.com/rapid7/r7-surcom-connectors -
Create a Python virtual environment:
python -m venv sdk-dev-env source sdk-dev-env/bin/activate -
Install the SDK:
python -m pip install r7-surcom-sdk
Task 3: Configure the SDK
-
Optionally, turn on tab auto complete:
surcom config enable-tabs -
Initialize the SDK configuration:
surcom config init -
Enter the path to your Connectors Workspace:
Choose the appropriate directory
The Connectors repository has directories for different developer types (for example, rapid7, partners). Externally-developed Connectors should go in the partners directory.
> ~/development/r7-surcom-connectors/connectors/partners-
Enter the URL for the Rapid7 Surface Command Platform (default:
us.surface.insight.rapid7.com):> us2.surface.insight.rapid7.com -
Enter a name for the connection (normally the name of your Organization). Connection name should follow the format:
<region>-<staging|prod|poc>-<companyname>(for example,eu-prod-examplecorp):> us-staging-rapid7 -
Enter the API Key. Rapid7 recommends using a 1Password Secret Reference. See the 1Password documentation for details. Plain text API keys are not recommended as they are saved directly to the configuration file.
-
Validate the configuration:
surcom config test
This creates a surcom_config file at ~/.r7-surcom-sdk/surcom_config with your initial connection configuration.
SDK command-line interface (CLI) reference
This section provides reference information for the surcom-sdk command-line interface (CLI). It includes usage syntax, available commands, options, and examples.
Command syntax and tree
usage:
$ surcom <command> ...
$ surcom -v <command> ...
$ surcom --version
options:
-h, --help show this help message and exit
-v, --verbose Set the log level to DEBUG
--version Print the version of the surcom SDK and exit
commands:
config Configure the surcom-sdk
connector Develop Connectors for the Rapid7 Surface Command Platform
type Manage Surface Command Types
data Interact with Surface Command Dataconfig
Configures the SDK.
Usage:
surcom config <command> [options]Commands:
| Command | Description | Options |
|---|---|---|
init | Creates an initial configuration file if none exists | None |
add | Adds a connection to the configuration file | None |
test | Tests the SDK configuration file | None |
delete | Deletes a connection from the configuration file |
|
list | Lists all configured connections | None |
set-active | Sets the active connection for testing |
|
enable-tabs | Enable tab auto-completion when using the SDK | None |
connector
Builds Connectors.
Usage:
surcom connector <command> [options]Commands:
| Command | Description | Options |
|---|---|---|
init | Initializes a new connector | None |
codegen | Generates basic code for a new connector |
|
invoke | Invokes a connector |
|
package | Packages a connector for submission |
|
validate | Validates a connector as it is configured |
|
type
Manages Surface Command types. To learn more about the Surface Command type system, see Explore the Attack Surface Management type system.
Usage:
surcom type <command> [options]Commands:
| Command | Description | Options |
|---|---|---|
generate | Generates a type definition |
|
install | Installs a type on your Surface Command instance |
|
data
Enables interaction with Attack Surface Management data.
Usage:
surcom data <command> [options]Commands:
| Command | Description | Options |
|---|---|---|
import | Imports type data into your Surface Command instance |
|
Build an example connector
The following sections build an example connector that fetches data from the Reqres service.
Want to see the example before getting started?
You can see the full example connector in the following directory: r7-surcom-connectors/connectors/rapid7/request_response_demo/
Task 1: Initialize the connector
-
Open a terminal.
-
Go to your connectors directory:
cd ~/development/r7-surcom-connectors/connectors/partners -
Initialize the connector:
surcom connector init -
Enter a display name (for example,
My Req Res Demo):> My Req Res Demo -
Enter a connector ID (for example,
my.req.res.demo):> my.req.res.demo -
Enter the author (for example,
username):> username
A template connector specification file (connector.spec.yaml) is generated in your workspace (defined by the configuration file).
Task 2: Modify the connector specification file
-
Open the connector specification file (
connector.spec.yaml) in the IDE of your choice. Rapid7 recommends using Microsoft Visual Studio Code. -
Replace the template types:
types: - MyReqResDemoUser - MyReqResDemoDevice -
Update the
import_allfunction:- id: import_all return_types: - MyReqResDemoUser - MyReqResDemoDevice -
Replace the
settingsblock:settings: url: type: string title: URL example: https://example.com default: https://reqres.in api_key: type: string title: API Key format: password verify_tls: type: boolean title: Verify TLS? description: If enabled, verify the server's identity default: true page_limit: type: integer title: Page Limit description: Maximum number of items to return per page enum: - 2 - 4 - 6 default: 2 # This is an optional setting that can be used to control pagination. total_pages: type: integer title: Total Pages description: >- Total number of pages to retrieve. Set it to 0 to retrieve all pages. default: 1 nullable: true
Task 3: Generate a basic connector structure
-
Open a terminal.
-
Go to the new connector directory:
cd ~/development/r7-surcom-connectors/connectors/partners/my_reqres_demo -
Generate basic template code for your connector:
surcom connector codegen -
Confirm file structure for the connector has been created.
Task 4: Add connector functions
Functions are located in my_reqres_demo/functions. For this example, you need to complete the following updates:
Update functions/helpers.py with a new get_devices method:
# Here is an example of a simple client that interacts with a third-party API.
class MyReqResDemoClient():
def __init__(
self,
user_log: Logger,
settings: Settings
):
# Expose the logger to the client
self.logger = user_log
# Expose the Connector Settings to the client
self.settings = settings
# Get the URL from the settings and ensure it is properly formatted
self.base_url = settings.get("url").strip().rstrip("/")
# Setup a Session using the Surcom HttpSession class
self.session = HttpSession()
# Use the value of our `verify_tls` setting to determine if we should verify TLS
self.session.verify = settings.get("verify_tls")
# Here we update the session header with the API Key from the Connector Settings.
# Authentication methods will vary based on the third-party API. Refer to the specific
# API documentation for details.
self.session.headers.update({
"x-api-key": settings.get("api_key")
})
def get_devices(self, page_number):
url = f"{self.base_url}/api/devices"
params = {
"per_page": self.settings.get("page_limit"),
"page": page_number
}
self.logger.debug("Requesting devices from '%s' with params: %s", url, params)
r = self.session.get(url, params=params)
r.raise_for_status()
return r.json()Update functions/fn_import_all.py with:
from logging import Logger
from . import helpers
from .sc_settings import Settings
from .sc_types import RequestResponseDemoDevice, RequestResponseDemoUser
def import_all(
user_log: Logger,
settings: Settings
):
user_log.info("Getting '%s' from '%s'", RequestResponseDemoDevice.__name__, settings.get("url"))
# Instantiate the RequestResponseDemoClient
client = helpers.RequestResponseDemoClient(user_log, settings)
# We set and keep track of the current page number
current_page = 1
while True:
# Get some assets from the client
r = client.get_devices(current_page)
data = r.get("data", [])
if data:
user_log.info("Processing %d items from page %d", len(data), current_page)
# For each asset in the response, yield a RequestResponseDemoDevice type to ingest
for asset in data:
# Ensure the ID is a string
if asset.get("id"):
asset["id"] = str(asset["id"])
yield RequestResponseDemoDevice(asset)
# If this is the last page, break out
# Otherwise, set the next page number to fetch
if r.get("page") == r.get("total_pages"):
user_log.debug("This is a debug log. We have reached the last page: %d", current_page)
break
else:
next_page = current_page + 1
user_log.debug("This is a debug log. Moving to the next page: %d", next_page)
current_page = next_pageTask 5: Update your configuration, create a type, and import data
-
Go to the Reqres website and click Get a Free API Key. Copy the key before closing the window.
- You can save the key as an API Credential in 1Password (recommended) so it can be accessed using a 1Password Secret Reference . Alternatively, you can save it as plain text (not recommended).
-
Invoke your connector function:
surcom connector invoke -f import_all -
Using the
invokecommand for the first time prompts you for the following settings:url: Accept the default (https://reqres.in).api_key: Enter the 1Password Secret Reference for your API key (recommended) or a plain text string (not recommended).verify_tls: Accept the default (True).page_limit: Accept the default (2).total_pages: Set to0.
This creates a new section (
[connector.my.req.res.demo]) in the configuration file (default:~/.r7-surcom-sdk/surcom_config). You can update this file or use the CLI to overwrite the settings. -
If you used a 1Password Secret Reference, authorize 1Password. After successful authorization, the output for the command is written to the
build/outputdirectory and a terminal message confirms this. -
Create a custom type:
surcom type generate build/output/MyReqResDemoDevice.json -
Import connector data into Surface Command:
surcom connector invoke -f import_all --import-data -
Confirm the operation.
-
Verify the type definition is correct imported into Surface Command by opening the Workspace and running the following query:
MATCH (r:RequestResponseDemoDevice) RETURN r -
Validate the connector and fix any critical issues.
surcom connector validate
Troubleshoot your connector
How can I debug my connector?
The Surface Command SDK also has optional debug functionality.
To install and configure the SDK debugger using Microsoft Visual Studio Code:
-
Open Microsoft Visual Studio Code and open an integrated terminal.
-
Install the extra debugger dependencies:
pip install 'r7-surcom-sdk[debug]' -
Open
.vscode/launch.jsonand copy and paste the example launch configuration. -
In the duplicate example launch configuration:
- Update
namewith the name of your connector. - Update the
pathMappings.localRootto the path of your connector’sfunctionscode.
- Update
-
Save
.vscode/launch.json. -
In the terminal, change directory to your connector:
cd ~/r7-surcom-connectors/connectors/partners/<connector-name> -
Run:
surcom connector invoke -f import_all --debug -
In the code window, add a breakpoint and when you see the terminal return
Debug mode enabled, waiting for debugger to attach, launch the debugger in Microsoft Visual Studio Code.
How can I turn on tab completion?
To turn on tab completion:
-
Run:
surcom config enable-tabs -
Restart your shell.
How do I find the surcom_config file?
surcom_config file?To find the location of the surcom-config file:
-
Run:
surcom config init
Configuration file hidden
The configuration file is hidden and prefixed with a .
How do I manage configuration file settings?
You can comment out a setting in the configuration file by prefixing with a -. For example, to ignore the total_pages setting:
[connector.my.req.res.demo]
url = https://reqres.in
api_key = "op://Employee/REQRES/FREE API KEY"
verify_tls = True
page_limit = 2
-total_pages = 0How do I turn on verbose logging?
To turn on verbose logging:
-
Add the
-vflag command. For example:surcom -v connector invoke -f import_all --import-data
How do I preview my documentation?
Microsoft Visual Studio Code has a built-in preview feature for Markdown files.
To preview your connector’s documentation:
- Open the
docs/INSTRUCTIONS.mdfile in Microsoft Visual Studio Code. - Right-click the file’s name in the file explorer.
- Click Open Preview.
How do I run a function locally?
Use the invoke command with the -f flag and its ID:
surcom connector invoke -f testHow do I overwrite the settings from the CLI?
Use the invoke command to pass any settings and overwrite values in the surcom_config file. For example:
surcom connector invoke -f import_all -s page_limit=4,api_key=xxx