Building Apps

Build Local Modules

Running tclib will download/install all required Python dependencies defined in the Apps requirements.txt file to a local lib_<version> directory.

Note

Typically calling tclib with no arguments is the most common use case. If building an App for distribution then using a configuration file to define the lib structure is preferable. The alternative is to run tclib using each Python version the App should support.

Warning

If you are using macOS and have Python installed via Homebrew, there is a known bug that requires you to create a setup.cfg file in the same directory as the requirements.txt. The setup.cfg file should have the following code at minimum:

[install]
prefix=

Usage

usage: tclib [-h] [--app_name APP_NAME] [--app_path APP_PATH]
             [--config CONFIG] [--no_cache_dir] [--tcex_develop]

optional arguments:
  -h, --help           show this help message and exit
  --app_name APP_NAME  Fully qualified path of App.
  --app_path APP_PATH  Fully qualified path of App.
  --config CONFIG      Configuration file for gen lib. (Default: tcex.json)
  --no_cache_dir       Do not use pip cache directory.
  --tcex_develop       Replace tcex version in config with github develop branch.

Using Configuration File

By default the tcex.json configuration file will be loaded if it exists. If the configuration includes the lib_versions parameter array the tclib command will use the values defined in the configuration to build the lib directories. For ease of management when building multiple Apps the tcex.json file can contain environment vars defining the Python version (e.g., $env.PY36 for export PY36='3.6.5').

Linux

<...snipped>
"lib_versions": [{
    "lib_dir": "lib_2.7.13",
    "python_executable": "~/.pyenv/versions/2.7.13/bin/python"
  },
  {
    "lib_dir": "lib_3.4.6",
    "python_executable": "~/.pyenv/versions/3.4.6/bin/python"
  },
  {
    "lib_dir": "lib_3.5.3",
    "python_executable": "~/.pyenv/versions/3.5.3/bin/python"
  },
  {
    "lib_dir": "lib_$env.PY36",
    "python_executable": "~/.pyenv/versions/$env.PY36/bin/python"
  }
],
<snipped...>

Windows

<...snipped>
"lib_versions": [{
    "lib_dir": "lib_2.7.13",
    "python_executable": "~\\AppData\\Local\\Programs\\Python\\Python27\\python.exe"
  }, {
    "lib_dir": "lib_3.6.2",
    "python_executable": "~\\AppData\\Local\\Programs\\Python\\Python36\\python.exe"
  }
],
<snipped...>

Testing Apps Locally

Apps are called from the ThreatConnect Platform using Command Line Interface Arguments or CLI Args. To simulate being called from ThreatConnect the TcEx tcrun command provides a method for converting a defined JSON structure to CLI Args. Running tcrun will read the tcex.json file and parse the args section from the default profile in the profiles parameter array. To specify a different file the --config argument can be passed (e.g. --config my_config.json). To run a specific profile the --profile argument can be passed (--profile add-indicator). A group of profiles can also be run using the --group argument (e.g. --group run-all).

Note

When testing a single profile (use case) the tcrun command would be called using that profile. Before packaging the App calling tcrun using a group that will run multiple profiles is typical.

Usage

usage: tcrun [-h] [--config CONFIG] [--autoclear] [--halt_on_fail]
             [--group GROUP] [--logging_level LOGGING_LEVEL]
             [--profile PROFILE] [--quiet] [--report REPORT]
             [--truncate TRUNCATE] [--unmask]

optional arguments:
  -h, --help            show this help message and exit
  --config CONFIG       The configuration file. (default: "tcex.json")
  --autoclear           Clear Redis data before running.
  --halt_on_fail        Halt on any failure.
  --group GROUP         The group of profiles to executed.
  --logging_level LOGGING_LEVEL
                        The logging level.
  --profile PROFILE     The profile to be executed. (default: "default")
  --quiet               Suppress output.
  --report REPORT       The JSON report filename.
  --truncate TRUNCATE   The length at which to truncate successful validation
                        data in the logs (default=50).
  --unmask              Unmask masked args.

Profile Format

Multiple testing profiles can be defined in the tcex.json to help testing coverage. Each profile should include a unique profile_name and a group which allows for running multiple profiles at once.

Note

The tcrun command provides a feature that allow using environment variables in the tcex.json configuration file. By prefixing the environment variable with $env. or $envs. the tcrun command will pull the value from the OS Environment variable. This feature helps facilitate quickly changing values between testing environments and/or user credentials.

Important

To protect credentials in the tcex.json configuration file the $envs. feature should be used. This will keep the credentials out of version control and will mask them on command output. To unmask the credentials temporarily the --unmask command can be passed to tcrun.

{
  "profiles": [{
    "args": {
      "api_access_id": "$env.TC_ACCESS_ID",
      "api_secret_key": "$envs.TC_SECRET_KEY",
      "logging": "debug",
      "tc_api_path": "$env.TC_API_PATH",
      "tc_log_path": "log",
      "tc_log_to_api": true,
      "tc_out_path": "log",
      "tc_playbook_db_type": "Redis",
      "tc_playbook_db_context": "7960ab08-26cb-4140-abb9-9310bb45ac86",
      "tc_playbook_db_path": "localhost",
      "tc_playbook_db_port": "6379",
      "tc_proxy_host": "10.10.10.10",
      "tc_proxy_port": "3128",
      "tc_proxy_external": true,
      "tc_proxy_tc": true,
      "tc_temp_path": "log",
      "message": "#App:1234:slack-string!String",
      "slack_api_token": "$envs.SLACK_API_TOKEN",
      "slack_recipient": "@bob"
    },
    "groups": ["run-all"],
    "install_json": "install.json",
    "quiet": false,
    "profile_name": "send-slack-message"
  }]
}

Note

The args section should contain all the CLI Args your app requires to run. The groups section defines which groups this profile should run under and the profile_name is the name which can be used to run the profile. The quiet field indicates whether the app should print output.

Profile Creation

The TcEx framework provides the tcprofile command to automatically generate a profile from the install.json file. The output of the tcprofile command will set the exit_code to 0 by default, however testing failure scenarios is also possible by setting the exit code to 1. Some default values will be added to the args section, but any custom inputs will need to be populated with the appropriate data.

For playbook Apps the tcprofile command will also create 2 standard validations for each output variable. The first validation will check to see if the output variable is null and the second will ensure the output variables is the correct type.

Note

The tcprofile command can be run multiple times to generate several different profiles with different input and/or output variables.

usage: tcprofile [-h] [--action {create,delete,replace_validation,update}]
                 [--ij IJ] [--data_file] [--outdir OUTDIR] [--outfile OUTFILE]
                 --profile_name PROFILE_NAME [--redis_hash REDIS_HASH]
                 [--redis_host REDIS_HOST] [--redis_port REDIS_PORT]

optional arguments:
  -h, --help            show this help message and exit
  --action {create,delete,replace_validation,update}
  --ij IJ               The install.json file name (default: install.json).
  --data_file           Attempt to build data file templates for the profile.
  --outdir OUTDIR       The *base* output directory containing the
                        data/profiles folder.
  --outfile OUTFILE     The name of the file to write the profile.
  --profile_name PROFILE_NAME
                        The profile name to create, delete, or update.
  --redis_hash REDIS_HASH
                        The redis hash.
  --redis_host REDIS_HOST
                        The redis host.
  --redis_port REDIS_PORT
                        The redis port.

Staging Redis Data

Important

A local instance of Redis must be running to test Playbook Apps locally.

In order to test using variable inputs the data can be manually added to Redis. The tcrun command has functionality to “stage” the data in redis that can be used to simulate an upstream App writing data to Redis. This staged data can be and added to a single json file or multiple reusable files. Once the files have been created they should be referenced in the Profile.

Example Data File

Note

Data files can contain a single data input or multiple data inputs. In most cases it better to have separate files so the data can be reused in multiple Profiles.

[{
  "data": [{
      "id": 125,
      "value": "threat001-build-testing",
      "type": "Threat",
      "ownerName": "qa-build",
      "dateAdded": "2017-08-16T18:45:42-04:00",
      "webLink": "https://mytc.myorg.com/auth/threat/threat.xhtml?threat=125"
    },
    {
      "id": 124,
      "value": "incident001-build-testing",
      "type": "Incident",
      "ownerName": "qa-build",
      "dateAdded": "2017-08-16T18:44:57-04:00",
      "webLink": "https://mytc.myorg.com/auth/incident/incident.xhtml?incident=124"
    },
    {
      "id": 123,
      "value": "doc001-build-testing",
      "type": "Document",
      "ownerName": "qa-build",
      "dateAdded": "2017-08-16T18:43:54-04:00",
      "webLink": "https://mytc.myorg.com/auth/document/document.xhtml?document=123"
    },
    {
      "id": 122,
      "value": "camp001-build-testing",
      "type": "Campaign",
      "ownerName": "qa-build",
      "dateAdded": "2017-08-16T18:40:56-04:00",
      "webLink": "https://mytc.myorg.com/auth/campaign/campaign.xhtml?campaign=122"
    },
    {
      "id": 116,
      "value": "adver001-build-testing",
      "type": "Adversary",
      "ownerName": "qa-build",
      "dateAdded": "2017-08-16T18:35:07-04:00",
      "webLink": "https://mytc.myorg.com/auth/adversary/adversary.xhtml?adversary=116"
    }
  ],
  "variable": "#App:0022:groups!TCEntityArray"
}]

Profile with Data File

[{
  "args": {
    "api_access_id": "$env.API_ACCESS_ID",
    "api_secret_key": "$envs.API_SECRET_KEY",
    "logging": "debug",
    "tc_api_path": "$env.TC_API_PATH",
    "tc_log_path": "log",
    "tc_log_to_api": true,
    "tc_out_path": "log",
    "tc_temp_path": "log",
    "tc_playbook_db_type": "Redis",
    "tc_playbook_db_context": "1860ab08-26cb-4140-abb9-9310bb45ac86",
    "tc_playbook_db_path": "localhost",
    "tc_playbook_db_port": "6379",
    "tc_playbook_out_variables": "#App:0072:tc.tag.fail_count!String,#App:0072:tc.tag.success_count!String,#App:0072:tc.tag.tags!StringArray",
    "entity": "#App:0022:groups!TCEntityArray",
    "tag": "QaTagCreate"
  },
  "data_files": [
    "tcex.d/data/groups.json"
  ],
  "description": "Pass test of create tag.",
  "group": "qa-build",
  "profile_name": "create-tag-on-groups",
  "quiet": false,
  "script": "tc_tag",
}]

Data Validation

The tcrun command provides some basic data validation for output variables. By defining the validations parameter array in the tcex.json file the tcrun command will pull the values from the REDIS DB and perform the provided operator on the data. This action simulates a downstream App reading the data from REDIS.

Example Configuration

"validations": [{
    "data": null,
    "operator": "ne",
    "variable": "#App:1073:tc.association.success_count!String"
  },
  {
    "data": "string",
    "operator": "it",
    "variable": "#App:1073:tc.association.success_count!String"
  },
  {
    "data": null,
    "operator": "ne",
    "variable": "#App:1073:tc.association.fail_count!String"
  },
  {
    "data": "string",
    "operator": "it",
    "variable": "#App:1073:tc.association.fail_count!String"
  }
]

Supported Operators

'eq' # equal to
'ew' # ends with
'ge' # greater than or equal to
'gt' # greater than
'in' # in array
'ni' # not in array
'it' # is type (array, binary, entity, string)
'lt' # less than
'le' # less than or equal to
'ne' # not equal
'sw' # start with

Exit Codes

The tcrun command can validate the exit code of the App. This allows for setting up fail scenarios profiles. All Apps should exit with a valid exit code and handle failures gracefully. Using the exit_codes parameter you can provide “bad” data to the App and ensure it exits with the proper exit code.

"exit_codes": [1]

Note

For Runtime/Job Apps (non Playbook Apps) valid exit codes are 0, 1, and 3. For certain profiles you may expect an exit code of 0 for success or 3 for partial success/partial failure. This can be achieved by adding both status codes to the exit_codes parameter array.

Packaging an App

Running tcpackage will build a zip package of your App that can be installed directly in the ThreatConnect Platform. For Apps packages that contain multiple Apps using separate install.json files add the --bundle argument to build a bundle. When using multiple install.json files the prefix of the filename is the App name (e.g. MyApp.install.json will have an App name of MyApp). During the build process validation of the install.json file will be handled automatically if the tcex_json_schema.json schema file is included in the base directory of your App.

Note

Typically the tcpackage command would be run with no arguments or using the --bundle flag for packages with multiple install.json files.

Usage

usage: tcpackage [-h] [--bundle] [--exclude EXCLUDE] [--config CONFIG]
                 [--install_json INSTALL_JSON] [--outdir OUTDIR]

optional arguments:
  -h, --help            show this help message and exit
  --bundle              Build a bundle file.
  --exclude EXCLUDE     File and directories to exclude from build.
  --config CONFIG       Build configuration file. (Default: tcex.json)
  --install_json INSTALL_JSON
                        The install.json file name for the App that should be
                        built.
  --outdir OUTDIR       Directory to write the outfile. (Default: target)

Configuration Options

By default the tcex.json file will be loaded by tcpackage. The following is an example of additional configuration that can be provided for the tcpackage command.

<..snipped>
"package": {
  "app_name": "MyApp",
  "app_version": "v1.0",
  "bundle": false,
  "excludes": [
    "log",
    "requirements.txt",
    "tcex.d"
  ],
  "outdir": "target"
},
<snipped..>

Multiple Bundles

<..snipped>
"package": {
  "bundle": true,
  "bundle_name": "TCPB_-_Palo_Alto",
  "bundle_packages": [{
    "name": "TCPB_-_My_Bundle_Create",
    "patterns": [
      ".*Create.*"
    ]
  },
  {
    "name": "TCPB_-_My_Bundle_Delete",
    "patterns": [
      ".*Delete.*"
    ]
  }],
  "excludes": [
    "log",
    "requirements.txt",
    "tcex.d"
  ],
  "outdir": "target"
},
<snipped..>