diff --git a/docs/integrations.json b/docs/integrations.json index 62374ad..4b17f13 100644 --- a/docs/integrations.json +++ b/docs/integrations.json @@ -1,5 +1,5 @@ { - "lastUpdated": "2026-03-20T15:10:59.030130Z", + "lastUpdated": "2026-03-26T14:58:16.731983Z", "totalIntegrations": 32, "integrationDetails": [ { diff --git a/lima-charlie/README.md b/lima-charlie/README.md index 152036a..d9880a5 100644 --- a/lima-charlie/README.md +++ b/lima-charlie/README.md @@ -24,8 +24,8 @@ ### runZero configuration 1. (OPTIONAL) - Make any necessary changes to the script to align with your environment. - - Modify API calls as needed to filter sensor data. - - Modify datapoints uploaded to runZero as needed. + - Set CUSTOM_ATTRIBS_TO_IGNORE. By default, sid, hostname, mac_addr, int_ip and ext_ip are ignored because they are redundant with core runZero attributes. All other attributes returned by API will be imported. + - Set boolean values in ARCHITECTURE to control what sensor architectures are imported. By default, chromium and usp_adapter sensors are not imported because they do not represent traditional cyber assets. 2. [Create the Credential for the Custom Integration](https://console.runzero.com/credentials). - Select the type `Custom Integration Script Secrets`. - Use the `access_key` field for your Lima Charlie Organization ID (`oid`). diff --git a/lima-charlie/custom-integration-lima-charlie.star b/lima-charlie/custom-integration-lima-charlie.star index 90ffd09..1005bad 100644 --- a/lima-charlie/custom-integration-lima-charlie.star +++ b/lima-charlie/custom-integration-lima-charlie.star @@ -7,59 +7,97 @@ load('uuid', 'new_uuid') LIMACHARLIE_JWT_URL = 'https://jwt.limacharlie.io' LIMACHARLIE_BASE_URL = 'https://api.limacharlie.io/v1' -def get_token(oid, access_token): - url = '{}/?oid={}&secret={}'.format(LIMACHARLIE_JWT_URL, oid, access_token) - token = http_post(url, headers={"Content-Type": "application/json"}) - if token.status_code != 200: +# Exclusion list for sensors that you want to ignore by hostname. +SENSORS_TO_IGNORE = [ + # sensor_hostname_01, + # sensor_hostname_02, + # sensor_hostname_03 +] + +# List of attributes that are not pulled into runZero. +# Note: sid, hostname, mac_addr, int_ip and ext_ip are imported as core asset attributes so +# they are ignored for the purpose of custom LimaCharlie attributes. +CUSTOM_ATTRIBS_TO_IGNORE = [ + 'sid', + 'hostname', + 'mac_addr', + 'int_ip', + 'ext_ip' +] + +# Filter based on what architectures you want to import into runZero +# By default, Chromium browsed based extensions are excluded from import +ARCHITECTURE = { + 1: True, # x86 + 2: True, # x64 + 3: True, # arm + 4: True, # arm64 + 5: True, # alpine64 + 6: False, # chromium + 7: True, # wireguard + 8: True, # arml + 9: False, # usp_adapter +} + +def get_token(oid, token): + url = '{}/?oid={}'.format(LIMACHARLIE_JWT_URL, oid) + headers = { + 'Content-Type': 'application/json', + 'X-LC-Secret': token + } + + response = http_post(url, headers=headers) + if response.status_code != 200: + print('Failed to fetch token. ', response) return None - token_json = json_decode(token.body) - return token_json['jwt'] + else: + response_json = json_decode(response.body) + return response_json['jwt'] def build_assets(sensors): assets = [] for item in sensors: - sid = item.get('sid', new_uuid) + sid = item.get('sid') hostname = item.get('hostname', '') + arch_id = item.get('arch', '') - ips = [] - int_ip = item.get('int_ip', '') - if int_ip: - ips.append(int_ip) - ext_ip = item.get('ext_ip', '') - if ext_ip: - ips.append(ext_ip) - - mac = item.get('mac_addr', '') - if mac: - mac = mac.replace("-", ":") - network = build_network_interface(ips=ips, mac=mac) + if hostname in SENSORS_TO_IGNORE: + print('Skipping sensor because it has been explicitly ignored in custom integration script:', sid, hostname) + elif not ARCHITECTURE.get(arch_id): + print('Skipping sensor because sensor architecture', arch_id, 'has been set to False in custom integration script:', sid, hostname) else: - network = build_network_interface(ips=ips, mac=None) - - # handle additional attributes collected for asset - custom_attrs = {} - - custom_attribs_to_ignore = [ - "sid", - "hostname", - "mac_addr", - "int_ip", - "ext_ip" - ] - - for key, value in item.items(): - if type(value) != 'dict': - if key not in custom_attribs_to_ignore: - custom_attrs[key] = str(value)[:1023] - - assets.append( - ImportAsset( - id=sid, - hostnames=[hostname], - networkInterfaces=[network], - customAttributes=custom_attrs + # Parse IPs and mac addresses and build network interfaces + ips = [] + int_ip = item.get('int_ip', '') + if int_ip: + ips.append(int_ip) + ext_ip = item.get('ext_ip', '') + if ext_ip: + ips.append(ext_ip) + + mac = item.get('mac_addr', '') + if mac: + mac = mac.replace("-", ":") + network = build_network_interface(ips=ips, mac=mac) + else: + network = build_network_interface(ips=ips, mac=None) + + # Parse additional attributes collected from sensors, ignore attributes defined in ATTRIBS_TO_IGNORE + custom_attrs = {} + for key, value in item.items(): + if type(value) != 'dict': + if key not in CUSTOM_ATTRIBS_TO_IGNORE: + custom_attrs[key] = str(value)[:1023] + + assets.append( + ImportAsset( + id=sid, + hostnames=[hostname], + networkInterfaces=[network], + customAttributes=custom_attrs + ) ) - ) + return assets def build_network_interface(ips, mac): @@ -82,21 +120,18 @@ def main(**kwargs): oid = kwargs['access_key'] access_token = kwargs['access_secret'] token = get_token(oid, access_token) - if not token: - print('failed to get token') - return None # Get sensors url = '{}/{}/{}'.format(LIMACHARLIE_BASE_URL, 'sensors', oid) sensors = http_get(url, headers={"Content-Type": "application/json", "Authorization": "Bearer " + token}) if sensors.status_code != 200: - print('failed to retrieve sensors') + print('Failed to fetch sensors. ', sensors) return None sensors_json = json_decode(sensors.body)['sensors'] assets = build_assets(sensors_json) if not assets: - print('no assets') + print('No sensors were retrieved.') return assets \ No newline at end of file