CCOM2 RESTful API Definition Copyright (C) 2020-2022, The AbedGraham Group. All rights reserved. Author: Dan Hagon Staging and Production Servers The production server can be accessed from https://ccom2-api.abedgraham.com. For testing client implementations there is a staging server at https://ccom2-apitest.abedgraham.com which is not covered by the same Service Level Agreement terms as the production server. As such, the following warning will be displayed in results returned on the staging server: USING STAGING ENVIRONMENT. RESULT ACCURACY IS NOT GUARANTEED. In the remainder of this documentation we’ll refer to the production server but the same API calls apply to the staging server. The same credentials work across both endpoints. Selecting and using different versions of the API Changes to the CCOM2 REST API are published on a quarterly release schedule. In general there are three concurrently supported versions of the API: • • • /beta/* will be used for early availability features prior to the next quarterly release. These are non-stable changes but allows customers to access new features and use them on their production data. /latest/* is the current recommended stable release of CCOM2 that the majority of users should be using. /lts/* (for long-term-support) gives customers more time to update their systems in preparation for changes in each quarterly release. In addition, the following aliases are defined: • • • • /_/* is an alias for /latest/* and is retained for backwards compatibility up until 30/06/2022 /v0.4.5/* is available as the following aliases: – for /beta/* up until 30/06/2022 – for /latest/* between 01/07/2022 up until 30/09/2022 – for /lts/* between 01/10/2022 up until 31/12/2022 /v0.4.4/* is available as the following aliases: – for /latest/* between 01/04/2022 up until 30/06/2022 – for /lts/* between 01/07/2022 up until 30/09/2022 /v0.4.3/* is available as the following aliases: – for /lts/* up until 30/06/2022 Documentation related to updates of the quarterly release schedule can be found by logging in to the CCOM2 Web UI. In the documentation below, where the URL is given as e.g.: GET https://ccom2-api.abedgraham.com/_/api/version the underscore _ part of the URL could be replaced by any of the resource version specifiers given above, e.g.: GET https://ccom2-api.abedgraham.com/latest/api/version or: GET https://ccom2-api.abedgraham.com/v0.4.5/api/version Authentication and Authorization OAuth2.0 Grants First sign up for a user account via https://ccom2.abedgraham.com. You will need a valid email account and a phone number which can receive SMS messages. Each request to the RESTful API must be accompanied by an API token using the Authorization header in the HTTP/1.1 request: Authorization: Bearer <Id Token> There are two options for obtaining this token, using OAuth2.0 grants: • • Authorization Code flow In this flow a client app receives a code from the authorization server which the client app then uses to obtain an access token. Implicit flow In this flow the access token is obtained directly, without the additional step of obtaining the code first. Tokens are valid for a short period of time so there is a Token Refresh flow that allows for refreshing tokens, but only the Authorization Code flow returns a refresh token. For Implicit flow, a new token must be obtained. The production and test endpoints use the following client ids: • • Production (https://ccom2-api.abedgraham.com) 1qe73lk2n2uq4has0f3s2e0anr Staging (https://ccom2-api-test.abedgraham.com) 4ic8pg3jeg4apvlhob3qr4hjt7 Additional details about the OAuth2.0 endpoint can be found in the Amazon Web Services Cognito documentation. Authentication Code flow The command-line steps here follow Authentication Code flow (without PKCE). First get a CSRF Token (e.g. cbf1fbed-ab93-4ddf-b195-6508aa233a7f): > GET /oauth2/authorize?response_type=code&client_id=1qe73lk2n2uq4has0f3s2e0anr&red irect_uri=https://ccom2-api.abedgraham.com/&scope=openid HTTP/1.1 > Host: ccom2-auth.abedgraham.com < HTTP/1.1 302 < location: https://ccom2auth.abedgraham.com/login?response_type=code&client_id=1qe73lk2n2uq4has0f3s2e 0anr&redirect_uri=https://ccom2-api.abedgraham.com/&scope=openid < set-cookie: XSRF-TOKEN=cbf1fbed-ab93-4ddf-b195-6508aa233a7f; Path=/; Secure; HttpOnly; SameSite=Lax Follow redirect and provide _csrf, username and password credentials, making sure they are correctly URL encoded, to obtain the authentication code: > POST /login?response_type=code&client_id=1qe73lk2n2uq4has0f3s2e0anr&redirect_uri=h ttps://ccom2-api.abedgraham.com/&scope=openid HTTP/1.1 > Host: ccom2-auth.abedgraham.com > cookie: XSRF-TOKEN=cbf1fbed-ab93-4ddf-b195-6508aa233a7f; Path=/; Secure; HttpOnly > content-type: application/x-www-form-urlencoded < HTTP/1.1 302 < location: https://ccom2-api.abedgraham.com/?code=623dbafe-b2f6-40da-8397a9bdf869c02e Use authentication code (e.g. 623dbafe-b2f6-40da-8397-a9bdf869c02e) displayed in the redirect URL to obtain a JWT, with data grant_type of authorization_code, client_id, (authorization) code and redirect_uri: > POST /oauth2/token HTTP/1.1 > Host: ccom2-auth.abedgraham.com > cookie: XSRF-TOKEN=cbf1fbed-ab93-4ddf-b195-6508aa233a7f; Path=/; Secure; HttpOnly > content-type: application/x-www-form-urlencoded < HTTP/1.1 200 < content-type: application/json;charset=UTF-8 {"id_token":"eyJraWQiOiIxWlJ...","expires_in":3600,"token_type":"Bearer"} Then the IdToken of the returned JWT can be used for Bearer authentication. Implicit Flow This requires response_type of token. The command-line steps here follow Implicit flow (without PKCE): > GET /oauth2/authorize?response_type=token&client_id=1qe73lk2n2uq4has0f3s2e0anr&re direct_uri=https://ccom2-api.abedgraham.com/&scope=openid HTTP/1.1 > Host: ccom2-auth.abedgraham.com < HTTP/1.1 302 < location: https://ccom2auth.abedgraham.com/login?response_type=token&client_id=1qe73lk2n2uq4has0f3s2 e0anr&redirect_uri=https://ccom2-api.abedgraham.com/&scope=openid < set-cookie: XSRF-TOKEN=cbf1fbed-ab93-4ddf-b195-6508aa233a7f; Path=/; Secure; HttpOnly; SameSite=Lax Follow redirect and provide _csrf, username and password credentials, making sure they are correctly URL encoded, to obtain the authentication code: > POST /login?response_type=token&client_id=1qe73lk2n2uq4has0f3s2e0anr&redirect_uri= https://ccom2-api.abedgraham.com/&scope=openid HTTP/1.1 > Host: ccom2-auth.abedgraham.com > cookie: XSRF-TOKEN=cbf1fbed-ab93-4ddf-b195-6508aa233a7f; Path=/; Secure; HttpOnly > content-type: application/x-www-form-urlencoded < HTTP/1.1 302 < location: https://ccom2api.abedgraham.com/#id_token=eyJraWQiOiJ6M0VBb...&access_token=eyJraWQiOiJ2eG xIc0NRdFM...&expires_in=3600&token_type=Bearer Use id_token from fragment in Location header to obtain a JWT that can be used for Bearer authentication. Token Refresh Flow Use the refresh token in the refresh_token parameter to obtain a JWT, with data grant_type of refresh_token and the appropriate client_id: > POST /oauth2/token HTTP/1.1 > Host: ccom2-auth.abedgraham.com > content-type: application/x-www-form-urlencoded < HTTP/1.1 200 < content-type: application/json;charset=UTF-8 {"id_token":"eyJraWQiOiIxWlJ...","expires_in":3600,"token_type":"Bearer"} Then the IdToken of the returned JWT can be used for Bearer authentication. Authenticate GET https://ccom2-api.abedgraham.com/_/api/authenticate Use this URI to obtain the user UUID, which is required for other URIs of the CCOM2 RESTful API; it requires the following header: Authorization: Bearer <Id Token> Responses: • HTTP Status Code: 200 {"user-uuid": "b8a393db-9e0b-337c-bf7e-c533b98b9a05"} If the bearer token has expired. • HTTP Status Code: 401 {"message": "The incoming token has expired"} If the user provides an incorrect username or password. • HTTP Status Code: 400 {"__type": "NotAuthorizedException", "message": "Incorrect username or password."} If the user is registered in the system but the user is not enabled. • HTTP Status Code: 400 {"__type": "NotAuthorizedException", "message": "User is disabled."} If authentication credentials (via bearer token) are required for a resource and are not provided or are invalid following response type will be issued: • HTTP Status Code: 401 {"errors": ["Unauthenticated", <Authentication error message>]} Most requests to the RESTful API require the UUID of the user in addition to the API key. When the user is not authorized to access a resource the following response will be returned: • HTTP Status Code: 403 {"errors": ["Unauthorized", <Authentication error message>]} Specific examples are given below: • HTTP Status Code: 403 {"errors": ["Unauthorized", "User account is not enabled!"]} If the above occurs please contact support at support@ccom2.net. API General Considerations String Encodings As per RFC 4627, the character encoding for returned JSON is UTF-8. Additionally, any freeform string will be HTML-encoded using character entity references. Date and time formats Since the JSON specification does not mandate a format for dates and times we adopt the format from ECMA-262 which is based on ISO 8601. Specifically dates should be of the format YYYY-MM-DDTHH:mm:ss.sssZ or YYYY-MM-DDTHH:mm:ss.sss+HH:mm as defined in section 21.4.1.15 of ECMA-262 (12th edition, June 2021). Malformed JSON Unless otherwise specified the header Content-Type: application/json and charset=utf-8 shall be used. In the case of malformed JSON within a request body or where Content-type isn’t application/json, the following response shall be returned: • HTTP Status Code: 400 {"errors": ["Bad request: Malformed JSON"]} Note: in the examples given in this document JSON requests and responses may contain additional whitespace for clarity. General system errors Bad requests that aren’t as a result of malformed JSON, such as when an incorrect MIME type is used or the payload is e.g. null and not a JSON object, result in the following: • HTTP Status Code: 400 {"errors": ["General Error: Bad request"]} If an authenticated or unauthenticated user attempts to access a resource that does not exist they will encounter the following: • HTTP Status Code: 404 {"errors": ["Not Found", "Resource not found"]} If an authenticated user attempts to access a resource that does not exist they will encounter one of the following: • HTTP Status Code: 404 {"errors": ["Not Found", "Namespace trust_1a.hospital_3b not found for user eff52988-f059-363d-b333-7464af942b70"]} • HTTP Status Code: 404 {"errors": ["Not Found", "Scan 98fa655fe0d3 not found for user eff52988-f059363d-b333-7464af942b70"]} • HTTP Status Code: 404 {"errors": ["Not Found", "Asset 98:be:94:f9:d2:2b not found for user eff52988-f059-363d-b333-7464af942b70"]} If a user attempts to access a resource using an incorrect method they will encounter the following: • HTTP Status Code: 405 {"errors": ["Method Not Allowed"]} General system errors are indicated by an HTTP 500 response. If you encounter such errors please contact AbedGraham support support@ccom2.net to help resolve the issue. • HTTP Status Code: 500 {"errors": ["Internal Server Error"]} Generally API responses will contain either a list of errors or a list of warnings or both. Gzip compression To reduce the size of payloads sent to CCOM2, use the following header to indicate that gzip compression is being used: Content-Encoding: gzip Similarly, responses will be sent using gzip compression if the following header is present in the HTTP request: Accept-Encoding: gzip It is strongly recommended that compression be used by default. RESTful API Definition Note: The examples given below are illustrative and may differ depending on usage. API Version Returns the current API version. GET https://ccom2-api.abedgraham.com/_/api/version Example responses: • HTTP Status Code: 200 {"version": "0.4.5"} Create namespace Creates a new namespace. Each submitted scan must be associated with a namespace and namespaces must be created before they are used. POST https://ccom2-api.abedgraham.com/_/api/:user_uuid/namespaces Note: the following legacy route is also supported: POST https://ccom2-api.abedgraham.com/_/api/:user_uuid/create-namespace Where: • user_uuid the UUID of user. Payload specification: {"namespace": "trust_1a.hospital_3b", "comment": "Example comment about the namespace"} Where the fields are defined as: • namespace a user-defined string to name a grouping of assets and their associated scans. An asset can appear in more than one namespace but assets in namespaces are siloed from assets in other namespace so that, e.g. trends in COFR scores can only be determined within a namespace and not across namespaces. An asset (as denoted by MAC address) must be unique within a given namespace and details of the asset can differ entirely between namespaces. A namespace must be an alpha numeric (a-z, A-Z, 0-9) string, including underscores (_), hyphens (-) or dots (.) up to 128 characters. The given example trust_1a.hospital_3b is suggestive of one option for naming and organizing namespaces but any such convention is entirely user-defined and not enforced by CCOM2. - comment A free-form field to allow for a more discursive description of the namespace. Example responses: If the new namespace was successfully created: - HTTP Status Code: 201 {"warnings": []} If the namespace identifier does not conform the format outlined above: - HTTP Status Code: 400 {"errors": ["Namespace has invalid format!"]} If the comment member of the JSON payload is not present: - HTTP Status Code: 400 {"errors": ["Missing 'comment' field!"]} If the comment member of the JSON payload is not a string: - HTTP Status Code: 400 {"errors": ["Expected 'comment' field is string!"]} If an attempt is made to create a new namespace using an identifier that already exists: HTTP Status Code: 400 {"errors": ["Cannot create duplicate namespace!"]} List namespaces Returns a list of previously created namespaces. GET https://ccom2-api.abedgraham.com/_/api/:user_uuid/namespaces Where: • user_uuid the UUID of user. Example responses: • HTTP Status Code: 200 {"namespaces": [ {"namespace": "trust_1a.hospital_1b", "comment": "Example comment about the namespace"}, {"namespace": "trust_1a.hospital_2b", "comment": "Another example comment about a namespace"}, {"namespace": "trust_1a.hospital_3b", "comment": "Yet another example comment about another namespace"} ]} Ingest a scan Ingest a scan of assets and their vulnerabilities. POST https://ccom2-api.abedgraham.com/_/api/:user_uuid/:namespace/scans Note: the following legacy route is also supported: POST https://ccom2-api.abedgraham.com/_/api/:user_uuid/:namespace/ingest-scan Where: • • user_uuid the UUID of user. namespace the namespace in which the scan/assets will be stored. Payload specification: Content-Type must be application/json and the body of the request must follow this format: {"comment": "Example comment", "assets": [{"mac_address": "12:34:56:78:90:ab", "ip_address": "192.168.0.1", "hostname": "rad-02-itu", "asset_name": "ACME X-Ray Machine", "user_assigned_label": "RAD02", "internal_id": "RAD02", "category": "X-Ray Machine", "manufacturer": "ACME Computer Corp.", "model": "Fanless PC", "location": "itu", "tags": ["personal_device", "patient_owned_device", "byod", "admin_group_abc12", "virtual_machine"], "availability": { "aggregate_cat": 12, "aggregate_cat_loc": 34, "spare_avail": 54, "patient_number": 14.5, "activity": 78 }, "network_connectivity": { "acl_policy": false, "device_outbound": true, "device_accessible": false, "connectivity_window": 14, "ehr_exchange": 0, "pacs_exchange": 1, "ris_exchange": 1, "edms_exchange": 3, "order_exchange": 0, "prescribe_exchange": 2, "lims_exchange": 1, "subnet_endpoint": 5, "internet_outbound": true, "internet_accessible": false, "subnet_vlan": 3, "connection_type": "ethernet", "isolated_network": true, "vendor_firewall": false, "vlan": false }, "ephi": { "creates": true, "receives": true, "stores": true, "transmits": true }, "vulns": [{"cve": "CVE-2020-123456", "cvss": 1.2}, {"cve": "CVE-2020-78901", "cvss": 3.4}, {"cve": "CVE-2020-65432", "cvss": 5.6} ] }, {"mac_address": "12:34:56:78:90:ac", "ip_address": "192.168.0.2", "asset_name": "ACME X-Ray Machine", "user_assigned_label": "RAD03", "category": "X-Ray Machine", "force_category_update": "true", "manufacturer": "ACME Computer Corp.", "model": "Fanless PC", "location": "itu", "tags": ["personal_device", "patient_owned_device", "byod", "admin_group_abc12", "virtual_machine"], "availability": { "aggregate_cat": 12, "aggregate_cat_loc": 34, "spare_avail": 54, "patient_number": 14.5, "activity": 78 }, "network_connectivity": { "acl_policy": false, "device_outbound": true, "device_accessible": false, "connectivity_window": 8, "ehr_exchange": 0, "pacs_exchange": 1, "ris_exchange": 1, "edms_exchange": 3, "order_exchange": 0, "prescribe_exchange": 2, "lims_exchange": 1, "subnet_endpoint": 5, "internet_outbound": true, "internet_accessible": false, "subnet_vlan": 3, "connection_type": "ethernet", "isolated_network": true, "vendor_firewall": false, "vlan": false }, "ephi": { "creates": true, "receives": true, "stores": true, "transmits": true }, "vulns": [{"cve": "CVE-2020-123456", "cvss": 1.2}, {"cve": "CVE-2020-65432", "cvss": 5.6} ] ] } } Where the fields are defined as follows: • Non-optional: – mac_address MAC Address such as 01:23:45:67:89:ab. – ip_address a string with the current asset IP address. – • • category a string describing this asset type, such as X-Ray Machine. See elsewhere in this documentation for details on how to determine which categories are valid in this field. – vulns a list of vulnerabilities found for this asset. If there are no vulnerabilities for this asset this field must still be present as an empty list. Within each vulnerability entry, the following are non-optional fields: • cve CVE identifier such as CVE-2020-123456. • cvss CVSS score such as 1.2. Non-optional, free-form: – asset_name a string describing the asset in human-readable form, canonical name. Optional1: – location the location, a string, of the asset within the institution. Should be one of the following; if a different value is used then the precision of the scoring is affected and so the system will issue a warning: • administrative Administrative (finance and back office) • secretarial Secretarial • a_and_e Emergency Room (e.g. A&E) • itu Critical care (e.g. high dependency/ITU units) • medical Medical ward • surgical Surgical ward • theatres Theatres • radiology Radiology • op Outpatients • pharmacy Pharmacy – aggregate_cat Aggregated total number of instances of asset category – aggregate_cat_loc Aggregated total number of instances of asset category (per location) – spare_avail Spare availability for the device - hours/168 per week – ehr_exchange Exchanges information with the EHR? Integer number of hops between this asset and EHR. – pacs_exchange Exchanges information with the PACS system? Integer number of hops between this asset and PACS. – ris_exchange Exchanges information with the RIS system? Integer number of hops between this asset and RIS. – edms_exchange Exchanges information with an EDMS (document management system)? Integer number of hops between this asset and EDMS. – order_exchange Exchanges information with the Order Comms system? Integer number of hops between this asset and Order Comms system. – prescribe_exchange Exchanges information with the e-prescribing system? Integer number of hops between this asset and e-prescribing system. – lims_exchange Exchanges information with Laboratory information system – – – – – (LIMS)? Integer number of hops between this asset and LIMS. isolated_network Physically isolated network? One of either true or false. vendor_firewall Vendor provided firewall. One of either true or false. vlan Is it on a VLAN? One of either true or false. activity Hour count activity/168 in last week connectivity_window number of hours of connectivity in a 168 hour period. – – – – – – – – – – subnet_endpoint Total endpoint count per subnet internet_outbound Communicates outbound to internet? One of either true or false. internet_accessible Direct accessible from internet? One of either true or false. subnet_vlan Number of other subnets/VLANs it communicates with patient_number Average number of patients treated per day. Decimal. connection_type Connection type, one of: • ethernet • wireless • terminal server • device gateway acl_policy Does ACL/Policy exist for this asset? One of either true or false. device_outbound Device connects outbound to internet? One of either true or false. device_accessibleDevice receives inbound connections from internet/accessible from internet? One of either true or false. tags List of user-supplied free-form strings to allow user categorization and identification. – • force_category_update Boolean flag where a value of true indicates details of the asset should be forcibly updated in the same way provided by the PUT method of the /_/api/:user_uuid/:namespace/assets/:asset_mac resource described elsewhere in this document. Used in case an asset was incorrectly described previously. Results given in previous scans are retained but subsequent scans will be referenced to this description of the asset. Optional1, free-form: – user_assigned_label a string describing the asset in human-readable form, non-canonical name, such as Bob's iPhone. – manufacturer a string describing asset manufacturer. – modela string describing the asset profile (or model). – hostnamea string typically representing the DNS hostname of the asset. – internal_ida string that is meaningful to the CCOM2 user. If this field is present it will be displayed in the results for this asset to allow CCOM2 users to more easily match results to their own database entries. Note: this field is not checked for e.g. format or uniqueness. The mac_address field will always be used as a unique identifier for the asset within a namespace. The optional1 ephi fields specify whether or not the asset creates, receives, stores or transmits electronic Protected Health Information: • • • • creates one of true or false receives one of true or false stores one of true or false transmits one of true or false Notes: 1 Although these fields are optional, the more of them that are available the more precise the scoring for the assets within this scan. Example responses: If the ingested scan has been accepted: - HTTP Status Code: 202 { "scan_id": "30e0c98e1724" "scan_status": "accepted", "errors": [ "Element 0: Asset not a JSON object", "Element 1: Member field 'mac_address' missing", "Element 2: Member field 'mac_address' must be a string", "Element 3: Malformed MAC address 'ac:5f:3e:eb:98:yz'", "Element 4: Malformed IP address '192.168.10.abc'", "Element 5: Unknown asset type 'X-Ray Machine - Other'", "Element 6: Vuln element 0 is not a JSON object", "Element 7: Member field 'cve' missing from vuln element 0", "Element 8: Member field 'cvss' missing from vuln element 0", "Element 9: Malformed CVE identifier for vuln element 1", "Element 10: Malformed CVE identifier 'CVE-2020-abc' for vuln element 0", 'Element 11: Malformed CVSS value', "Element 12: Non-positive CVSS value '-1.2' for vuln element 0", "Element 13: CVSS value '12.0' greater than 10.0 for vuln element 0" "Element 14: Value '-14' of field 'connectivity_window' is negative", "Element 15: Value '-78' of field 'activity' falls outside expected range [0, 168]" ], "warnings": [ "Element 16: Member field 'location' missing", "Element 17: Member field 'user_assigned_label' must be a string", "Element 18: Member field 'ephi' must be an object", "Element 19: Member field 'acl_policy' must be a boolean", "Element 20: Member field 'pacs_exchange' must be a number", "Element 21: Member field 'tags' must be an array", 'Element 22: Tag 0 must be a string', "Element 23: Unexpected connection type 'carrier_pigeon'", "Element 24: Duplicate asset '12:a4:56:78:90:ab' removed, replaced with element 25", "Element 26: Previous version of asset '12:34:56:78:90:ab' will be replaced with this element" ], "assets_staged": 123, "assets_scanned": 0, "vulnerabilities": 0, "vulns_scanned": 0 } Where: scan_id can be used in subsequent requests to either PATCH the scan prior to processing or GET the reults of the processed scan. The possible scan_status values are one of: - rejected where the scan in its entirety has been rejected by the system, for instance if the assets member field is missing from the top-level JSON object - accepted where the scan has been accepted by the system but not yet submitted for processing. You should check any of the errors and warnings and if necessary PATCH the scan to correct them prior to submitting for processing. The following member fields provide statistics about the ingested scan: - assets_staged: the number of assets that are currently staged ready for processing. Note that the number of assets that are staged may not be the same as the number of assets that are eventually processed since problems, such as missing or malformed fields could mean the asset is rejected during processing. - assets_scanned: the total number of assets (with vulnerabilities) that have been successfully processed, not including assets with residual risk scores - vulns_scanned: the total number of vulnerabilities, across all assets, that have been successfully processed - vulnerabilities: an alias for vulns_scanned for backward compatibility If the scan payload is valid JSON object but but does not contain the assets member field: HTTP Status Code: 400 { "scan_id": "243e170e0c98" "scan_status": "rejected", "errors": [ "Scan cannot be a JSON array, it must be a JSON object" ], "warnings": [], "assets_staged": 0, "assets_scanned": 0, "vulnerabilities": 0, "vulns_scanned": 0 } If the assets member is missing from the scan payload: - HTTP Status Code: 400 { "scan_id": "e172430e0c98" "scan_status": "rejected", "errors": [ "Member 'assets' missing from the top-level scan JSON object" ], "warnings": [], "assets_staged": 0, "assets_scanned": 0, "vulnerabilities": 0, "vulns_scanned": 0 } If the scan payload has more then 512 assets the following will be encountered: - HTTP Status Code: 413 { "errors": [ "Number of assets in payload exceeds maximum of 512" ] } In these circumstances the request was not processed by CCOM2 and it should be re-sent in smaller batches, initially using the POST method followed by the PATCH method. If the payload of the scan is larger than 10MB the following might be encountered: - HTTP Status Code: 413 {"message": "Request Too Long"} In these circumstances the request was not processed by CCOM2 and it should be re-sent using one or any a combination of two options. The first option is to use gzip compression to reduce the size of the submitted payload: Content-Encoding: gzip However, if this response continues to be encountered the scan should be re-sent in smaller chunks as follows. First, use the POST method, as described here, to send a smaller subset of assets. Then subsequently the PATCH method should be used as many times as necessary to provide the remaining assets in the scan. The scan won’t be processed until the GET method is called for the first time, after which it will no longer be possible to add assets to the scan. Any staged scan left unprocessed for more than 24 hours will automatically be processed after this period has elapsed. In the case where a scan takes longer than 30 seconds to ingest: - HTTP Status Code: 500 An exception was raised. Check the application logs for details. Adding assets to an incomplete scan Once a scan has been initially ingested using the POST method, additional assets can be added to the scan, via calling the POST method first so long as the scan has not already been processed: PATCH https://ccom2api.abedgraham.com/_/api/:user_uuid/:namespace/scans/:scan_id The payload specification is the same as that described for the POST method. However, any valid updates to the comment field override any previous values submitted for this member field. Similarly, if an asset, as identified by its MAC address, has already been submitted for this scan, any subsequent valid instances that are submitted will completely override any previous details submitted for this scan. Example responses: If the scan update has been accepted: - HTTP Status Code: 202 { "scan_status": "accepted", "patch_status": "accepted", "errors": [], "warnings": [ "Previous scan comment removed, replaced with comment from this patch", "Element 0: Duplicate asset '12:34:56:78:90:ab' removed, replaced with element 1", "Element 1: Previous version of asset '12:34:56:78:90:ab' will be replaced with this element", "Element 2: Previous version of asset '12:a4:56:78:90:ab' will be replaced with this element" ], "assets_staged": 2, "assets_scanned": 0, "vulnerabilities": 0, "vulns_scanned": 0 } Where: • patch_status indicates how the system responsed to processing the patch, which is distinct from scan_status. For instance, a patch that is rejected will not change the original scan_status. The possible values of scan_status are the same as those returned via the GET method. The possible patch_status values are one of: • rejected where the patch in its entirety has been rejected by the system, for instance if the assets member field is missing from the top-level JSON object. The staged scan is unaffected by the submitted scan in this circumstance. • accepted where the patch has been accepted by the system and applied to the staged scan. You should check any of the errors and warnings and if necessary submmit another patch to correct them prior to submitting the staged scan for processing. The remaining member fields are the same as those described for the POST method although note that scan_id is not included. If the update has been submitted for a scan that does not exist for this user in this namespace: - HTTP Status Code: 403 {"errors": ["Unauthorized", "Scan 98fa655fe0d3 not found for user eff52988f059-363d-b333-7464af942b70"]} If the originally submitted scan was rejected the following will be returned since updates will no longer be accepted: - HTTP Status Code: 409 { "scan_status": "rejected", "patch_status": "rejected", "errors": [ "Cannot PATCH a rejected scan." ], "warnings": [], } "assets_staged": 0, "assets_scanned": 0, "vulnerabilities": 0, "vulns_scanned": 0 } If the scan has already been submitted for processing the following will be returned since updates will no longer be accepted: - HTTP Status Code: 409 { "scan_status": "accepted", "patch_status": "rejected", "errors": [ "Scan has already been processed." ], "warnings": [], "assets_staged": 2, "assets_scanned": 0, "vulnerabilities": 0, "vulns_scanned": 0 } Any staged scan left unprocessed for more than 24 hours will automatically be processed after this period has elapsed. If the scan payload has more then 512 assets the following will be encountered: - HTTP Status Code: 413 { "errors": [ "Number of assets in payload exceeds maximum of 512" ] } In these circumstances the request was not processed by CCOM2 and it should be re-sent in smaller batches. If the payload of the scan update is larger than 10MB the following might be encountered: HTTP Status Code: 413 {"message": "Request Too Long"} In these circumstances the request was not processed by CCOM2 and not updates will have been made to the scan. The update should be re-sent, for instance, by splitting into smaller subsets of assets or using gzip compression to reduce the size of the submitted payload: Content-Encoding: gzip In the case where a scan takes longer than 30 seconds to ingest: - HTTP Status Code: 500 An exception was raised. Check the application logs for details. List of scans Returns list of a previously submitted scans, in date order. GET https://ccom2api.abedgraham.com/_/api/:user_uuid/:namespace/scans?since=<dateimte>&limit=< int>&offset=<int> Where: • • • • • user_uuid the UUID of user. namespace the namespace in which the scans are found. since an optional field that specifies the date from which results should start. Incorrectly formatted values are ignored; see Date and time formats section for details. limit an optional field that specifies the maximum number of records to include in response, upto 1000 record; if not present or incorrectly formatted as a positive integer it defaults to 1000 records. offset an optional field that specifies the positive integer number of records to skip before returning results; if not present or incorrectly formatted as a positive integer it defaults to 0 records. Example responses: • HTTP Status Code: 200 {"scans": [ {"scan_id": "30e0c98e1724", "comment": "Example comment", "scan_date": "2020-05-20T12:34:56.789Z", "scan_status": "completed_ok" }, {"scan_id": "28977b5b5374"} "comment": "Example comment 2", "scan_date": "2020-05-20T12:34:57.789Z", "scan_status": "processing" } ] } Details of a scan Returns details of a previously submitted scan. GET https://ccom2api.abedgraham.com/_/api/:user_uuid/:namespace/scans/:scan_id?summary=<any> Where: • • • • user_uuid the UUID of user. namespace the namespace in which the scan is found. scan_id the id (hash) of the scan to be returned. summary an optional field that where present requests that only a summary of the scan be returned rather that the complete set of results. Any non-empty value e.g. true can be used with this parameter. For very large scans it is recommended that a summary be requested first and then the details of assets, error and warnings can be requested using the paging parameters for their respective routes. The output of CCOM2 is broadly split into two depending on whether assets have active vulnerabilities or not. For assets with vulnerabilities (under the assets field), scores between 1 and 12 will be produced for the Clinical, Organisational, Financial, Regulatory and Total columns (c, o, f, r and t fields). This list is meant to be for prioritisation of assets with active vulnerabilities, most likely to be handled by the security teams within an organisation. For assets without vulnerabilities (under the assets_residual field), a mark of minor, moderate or major will be produced for the Clinical, Organisational, Financial, Regulatory and Total columns. This list is meant to be for prioritisation of assets without active vulnerabilities based on their residual risk (to inform, for example, security planning, ISO status, and continuity and recovery work). These lists should be considered as separate ways of looking at risk in an organisation and should not be conflated together or shown in the same list in any visualisation. Note: calling this method will result in a scan being submitted for processing which means that further updates via the PATCH method will be rejected. Example responses: The possible scan_status values are are: - rejected - accepted - processing completed_unrecoverable_error - completed_recoverable_error - completed_ok In addition to the statistics given in response to scan ingest, the following are also provided: - count_assets the total number of assets (with vulnerabilities) that were successfully processed - count_assets_residual the total number of assets (without vulnerabilities) that were successfully processed - count_errors the total number of errors encountered during ingest and processing - count_warnings the total number of warnings encountered during ingest and processing Note: each of these number may be different from the number of assets given in the assets field of the result depending on the parameters given in the request; the are provided to allow paging through results. Note also: count_assets and count_assets_residual may not be present in response until processing of scan has completed. A scan that has just been accepted or is still being processed will return: - HTTP Status Code: 202 {"scan_status": "processing", "warnings": [], "errors": [], "count_warnings": 25, "count_errors": 0 } A completed scan with an unrecoverable error will return: - HTTP Status Code: 500 {"scan_status": "completed_unrecoverable_error", "warnings": [], "errors": ["Internal Server Error"], "count_warnings": 25, "count_errors": 0, "count_assets": 100 } Requesting a scan with the summary parameter will return: - HTTP Status Code: 200 {"scan_status": "completed_ok", "count_warnings": 250, "count_errors": 0, "comment": "Example comment", "scan_date": "2020-05-20T12:34:56.789Z", "institutional_risk_status": { "clinical_risk_status": 1, "organisational_risk_status": 2, "financial_risk_status": 3, "regulatory_risk_status": 4, "total_risk_status": 5 }, "assets_staged": 100000, "count_assets": 100000, "assets_scanned": 100000, "vulnerabilities": 456700, "vulns_scanned": 456700 } A completed scan with a recoverable error or where no error was encountered will return: - HTTP Status Code: 200 {"scan_status": "completed_ok", "count_warnings": 0, "count_errors": 0, "count_assets": 2, "warnings": [], "errors": [], "comment": "Example comment", "scan_date": "2020-05-20T12:34:56.789Z", "institutional_risk_status": { "clinical_risk_status": 1, "organisational_risk_status": 2, "financial_risk_status": 3, "regulatory_risk_status": 4, "total_risk_status": 5 }, "assets": [ { "mac_address": "01:23:45:67:89:ab", "scan_ip": "192.168.0.1", "scan_asset_name": "ACME X-Ray Machine", "scan_internal_id": "RAD02", "risk_breakdown": { "c": 2, "o": 4, "f": 6, "r": 8, "total": 10 }, "vulnerabilities": [ { "cve": "CVE-2020-123456", "cvss": 5.7, "risk_breakdown": { "c": 1, "o": 2, "f": 3, "r": 4, "total": 5 } }, { "cve": "CVE-2020-123456", "cvss": 5.7, "risk_breakdown": { "c": 1, "o": 2, "f": 3, "r": 4, "total": 5 } } ] } ], "assets_residual": [ { "mac_address": "01:23:45:67:89:ac", "scan_ip": "192.168.0.2", "scan_asset_name": "ACME X-Ray Machine", "scan_internal_id": "RAD03", "risk_breakdown": { "c": "minor", , "o": "moderate", "f": "major", "r": "minor", "total": "moderate" } } ] } Note: to ensure the response payload has a predictable size only a maxium of 1000 records each for assets, assets_residual, errors and warnings will be returned. To page through the complete set of assets, assets_residual, errors or warnings see sections Assets within a scan, Residual Assets within a scan, Errors within a scan and Warnings within a scan respectively. If the ingested scan was originally rejected this will result in the following response: - HTTP Status Code: 400 { "assets_scanned": 0, "assets_staged": 0, "count_errors": 1, "count_warnings": 0, "errors": [ "Member 'assets' missing from the top-level scan JSON object" ], "scan_status": "rejected", "vulnerabilities": 0, "vulns_scanned": 0, "warnings": [] } If the scan does not exist this will result in the following response: - HTTP Status Code: 403 {"errors": ["Unauthorized", "Scan 98fa655fe0d3 not found for user eff52988f059-363d-b333-7464af942b70"]} Assets within a scan Returns details of assets within a previously submitted scan, sorted by MAC address. GET https://ccom2api.abedgraham.com/_/api/:user_uuid/:namespace/scans/:scan_id/assets?limit=<i nt>&offset=<int> Where: • • • • user_uuid the UUID of user. namespace the namespace in which the scan/asset is found. scan_id the id (hash) of the scan to be returned. limit an optional field that specifies the maximum number of records to include in • response, upto 1000 records ; if not present or incorrectly formatted as a positive integer it defaults to 1000 records. offset an optional field that specifies the positive integer number of records to skip before returning results; if not present or incorrectly formatted as a positive integer it defaults to 0 records. Example responses: • HTTP Status Code: 200 {"assets": [ {"mac_address": "01:23:45:67:89:ab", "scan_ip": "192.168.0.1", "scan_asset_name": "ACME X-Ray Machine", "scan_internal_id": "RAD02", "risk_breakdown": { "c": 2, "o": 4, "f": 6, "r": 8, "total": 10 }, "vulnerabilities": [ {"cve": "CVE-2020-123456", "cvss": 5.7, "risk_breakdown": { "c": 1, "o": 2, "f": 3, "r": 4, "total": 5 } }, {"cve": "CVE-2020-123456", } } "cvss": 5.7, "risk_breakdown": { "c": 1, "o": 2, "f": 3, "r": 4, "total": 5 } } ] ] } Where a scan has not yet been fully processed, the following response will be encountered: - HTTP Status Code: 202 Where the possible scan_status values are as given in the scan summary. {"scan_status": "processing", "warnings": [], "errors": []} If the scan does not exist, the following response will be returned: - HTTP Status Code: 403 {"errors": ["Unauthorized", "Scan 98fa655fe0d3 not found for user eff52988f059-363d-b333-7464af942b70"]} Residual Assets within a scan Returns details of residual risk assets within a previously submitted scan, sorted by MAC address. GET https://ccom2api.abedgraham.com/_/api/:user_uuid/:namespace/scans/:scan_id/assets_residual ?limit=<int>&offset=<int> Where: • • • • user_uuid the UUID of user. namespace the namespace in which the scan/asset is found. scan_id the id (hash) of the scan to be returned. limit an optional field that specifies the maximum number of records to include in • response, upto 1000 record; if not present or incorrectly formatted as a positive integer it defaults to 1000 records. offset an optional field that specifies the positive integer number of records to skip before returning results; if not present or incorrectly formatted as a positive integer it defaults to 0 records. Example responses: • HTTP Status Code: 200 {"assets_residual": [ {"mac_address": "01:23:45:67:89:ac", "scan_ip": "192.168.0.2", "scan_asset_name": "ACME X-Ray Machine", "scan_internal_id": "RAD03", "risk_breakdown": { "c": "minor", , "o": "moderate", "f": "major", "r": "minor", "total": "moderate" } } ] } Where a scan has not yet been fully processed, the following response will be encountered: - HTTP Status Code: 202 Where the possible scan_status values are as given in the scan summary. {"scan_status": "processing", "warnings": [], "errors": []} If the scan does not exist, the following response will be returned: - HTTP Status Code: 403 {"errors": ["Unauthorized", "Scan 98fa655fe0d3 not found for user eff52988f059-363d-b333-7464af942b70"]} Errors within a scan Returns details of errors generated whilst processing a previously submitted scan, sorted lexicographically. GET https://ccom2api.abedgraham.com/_/api/:user_uuid/:namespace/scans/:scan_id/errors?limit=<i nt>&offset=<int> Where: • • • • user_uuid the UUID of user. namespace the namespace in which the scan/asset is found. scan_id the id (hash) of the scan to be returned. limit an optional field that specifies the maximum number of records to include in • response, upto 1000 record; if not present or incorrectly formatted as a positive integer it defaults to 1000 records. offset an optional field that specifies the positive integer number of records to skip before returning results; if not present or incorrectly formatted as a positive integer it defaults to 0 records. Example responses: • HTTP Status Code: 202 Where the possible scan_status values are as given in the scan summary. {"scan_status": "processing", "errors": []} If the scan does not exist, the following response will be returned: - HTTP Status Code: 403 {"errors": ["Unauthorized", "Scan 98fa655fe0d3 not found for user eff52988f059-363d-b333-7464af942b70"]} Warnings within a scan Returns details of warnings generated whilst processing a previously submitted scan, sorted lexicographically. GET https://ccom2api.abedgraham.com/_/api/:user_uuid/:namespace/scans/:scan_id/warnings?limit= <int>&offset=<int> Where: • • • • user_uuid the UUID of user. namespace the namespace in which the scan/asset is found. scan_id the id (hash) of the scan to be returned. limit an optional field that specifies the maximum number of records to include in • response, upto 1000 record; if not present or incorrectly formatted as a positive integer it defaults to 1000 records. offset an optional field that specifies the positive integer number of records to skip before returning results; if not present or incorrectly formatted as a positive integer it defaults to 0 records. Example responses: • HTTP Status Code: 202 Where the possible scan_status values are as given in the scan summary. {"scan_status": "processing", "warnings": []} If the scan does not exist, the following response will be returned: - HTTP Status Code: 403 {"errors": ["Unauthorized", "Scan 98fa655fe0d3 not found for user eff52988f059-363d-b333-7464af942b70"]} Details of an individual asset within a scan based on MAC Address Returns details an asset within of a previously submitted scan. GET https://ccom2api.abedgraham.com/_/api/:user_uuid/:namespace/scans/:scan_id/assets/:asset_m ac Where: • • • • user_uuid the UUID of user. namespace the namespace in which the scan/asset is found. scan_id the id (hash) of the scan to be returned. asset_mac the asset”s MAC address, such as 12:34:56:78:90:ab. Example responses: • HTTP Status Code: 200 {"mac_address": "01:23:45:67:89:ab", "scan_ip": "192.168.0.1", "scan_asset_name": "ACME X-Ray Machine", "scan_internal_id": "RAD02", "risk_breakdown": { "c": 1, "o": 2, "f": 3, "r": 4, "total": 5 } } If the scan has not yet been submitted for processing or completed processing, the following response will be returned: - HTTP Status Code: 404 {"scan_status": "accepted", "assets_staged": 1, "assets_scanned": 0, "vulnerabilities": 0, "vulns_scanned": 0, "count_errors": 1, "count_warnings": 0, "errors": ["Asset 12:34:56:78:90:ab from scan 6fc03955c361 not yet processed!"], "warnings": [] } • • • Where the possible scan_status values are are: accepted processing If the scan has been processed but there was an error with processing the asset then the following response will be returned: - HTTP Status Code: 404 { "errors": [ "Not Found", "Asset 12:34:56:78:90:ab not found for scan c32cc7abd1c7" ] } (Note: if the error "Asset type of asset 12:34:56:78:90:ab has changed!" is found in the overall scan result, consider using the facility described in Update Asset Details.) If the scan is not available in the given namespace for the given user then the following response will be returned: - HTTP Status Code: 403 {"errors": ["Unauthorized", "Scan 98fa655fe0d3 not found for user eff52988f059-363d-b333-7464af942b70"]} This error might also be encountered if the asset was not successfully staged due to an earlier error during ingest. Details of an individual asset (with residual risk) within a scan based on MAC Address Returns details an asset within of a previously submitted scan. GET https://ccom2api.abedgraham.com/_/api/:user_uuid/:namespace/scans/:scan_id/assets_residual /:asset_mac Where: • • • • user_uuid the UUID of user. namespace the namespace in which the scan/asset is found. scan_id the id (hash) of the scan to be returned. asset_mac the asset”s MAC address, such as 12:34:56:78:90:ab. Example responses: • HTTP Status Code: 200 {"mac_address": "01:23:45:67:89:ac", "scan_ip": "192.168.0.2", "scan_asset_name": "ACME X-Ray Machine", "scan_internal_id": "RAD02", "risk_breakdown": { "c": "minor", , "o": "moderate", "f": "major", "r": "minor", "total": "moderate" } } If the scan has not yet been submitted for processing or completed processing, the following response will be returned: - HTTP Status Code: 404 {"scan_status": "accepted", "assets_staged": 1, "assets_scanned": 0, "vulnerabilities": 0, "vulns_scanned": 0, "count_errors": 1, "count_warnings": 0, "errors": ["Asset 12:34:56:78:90:ab from scan 6fc03955c361 not yet processed!"], "warnings": [] } • • • Where the possible scan_status values are are: accepted processing If the scan has been processed but there was an error with processing the asset then the following response will be returned: - HTTP Status Code: 404 { "errors": [ "Not Found", "Asset 12:34:56:78:90:ab not found for scan c32cc7abd1c7" ] } (Note: if the error "Asset type of asset 12:34:56:78:90:ab has changed!" is found in the overall scan result, consider using the facility described in Update Asset Details.) If the scan is not available in the given namespace for the given user then the following response will be returned: - HTTP Status Code: 403 {"errors": ["Unauthorized", "Scan 98fa655fe0d3 not found for user eff52988f059-363d-b333-7464af942b70"]} This error might also be encountered if the asset was not successfully staged due to an earlier error during ingest. List of assets Returns lists of a assets. GET https://ccom2-api.abedgraham.com/_/api/:user_uuid/:namespace/assets Where: • • user_uuid the UUID of user. namespace the namespace in which the scan/asset is found. Example responses: • HTTP Status Code: 200 {"assets": [{"mac_address": "01:23:45:67:89:ab"}, {"mac_address": "fe:24:45:67:89:ab"} ]} List of asset vulnerabilities by MAC Address Returns details of an asset, including any vulnerabilities given in submitted scans. GET https://ccom2api.abedgraham.com/_/api/:user_uuid/:namespace/assets/:asset_mac Where: • • • user_uuid the UUID of user. namespace the namespace in which the scan/asset is found. asset_mac the asset’s MAC address, such as 12:34:56:78:90:ab. Example responses: • HTTP Status Code: 200 {"asset": {"mac_address": "01:23:45:67:89:ab", "vulnerabilities": [ {"scan_id": "30e0c98e1724", "scan_ip": "192.168.0.1", "scan_internal_id": "RAD03", "scan_asset_name": "ACME X-Ray Machine", "scan_manufacturer": "ACME", "scan_model": "Model XYZ-123", "scan_user_assigned_label": "RAD03", "scan_usertags": ["personal_device"] "cve": "CVE-2020-123456", "cvss": 5.7 }, {"scan_id": "28977b5b5374", "scan_ip": "192.168.0.1", "scan_internal_id": "RAD03", "scan_asset_name": "ACME X-Ray Machine", "scan_manufacturer": "ACME", "scan_model": "Model XYZ-123", "scan_user_assigned_label": "RAD03", "scan_usertags": ["virtual_machine"] "cve": "CVE-2020-123456", "cvss": 5.7 } ] } } • HTTP Status Code: 403 {"errors": ["Unauthorized", "Asset 98:be:94:f9:d2:2b not found for user eff52988-f059-363d-b333-7464af942b70"]} Update Asset Details Update details of an asset. PUT https://ccom2api.abedgraham.com/_/api/:user_uuid/:namespace/assets/:asset_mac Used in case an asset was incorrectly described previously. Results given in previous scans are retained but subsequent scans will be referenced to this description of the asset. Note: it’s possible to change e.g. the category of an asset between scans automatically by using the force_category_update field during scan ingest. Where: • • • user_uuid the UUID of user. namespace the namespace in which the asset is found. asset_mac the asset’s MAC address, such as 12:34:56:78:90:ab. Payload specification: Content-Type must be application/json and the body of the request must follow this format: {"asset": {"mac_address": "12:34:56:78:90:ab", "ip_address": "192.168.0.1", "asset_name": "ACME X-Ray Machine", "user_assigned_label": "RAD02", "category": "X-Ray Machine", "manufacturer": "ACME Computer Corp.", "model": "Fanless PC", "location": "itu", "tags": ["personal_device", "patient_owned_device", "byod", "admin_group_abc12", "virtual_machine"], "availability": { "aggregate_cat": 12, "aggregate_cat_loc": 34, "spare_avail": 54, "patient_number": 14.5, "activity": 78 }, "network_connectivity": { "acl_policy": false, "device_outbound": true, "device_accessible": false, "connectivity_window": 14, "ehr_exchange": 0, "pacs_exchange": 1, "ris_exchange": 1, "edms_exchange": 3, "order_exchange": 0, "prescribe_exchange": 2, "lims_exchange": 1, "subnet_endpoint": 5, "internet_outbound": true, "internet_accessible": false, "subnet_vlan": 3, "connection_type": "ethernet", "isolated_network": true, "vendor_firewall": false, "vlan": false } }, "ephi": { "creates": true, "receives": true, "stores": true, "transmits": true } } Where the fields are defined as given in the section on ingesting a scan). (Please note, however, the force_category_update field is ignored and assumed to have the value true for the purposes of this particular request.) Example responses: If there is a problem with the format or fields of the updated asset then responses similar to the following will be encountered: - HTTP Status Code: 400 {"errors": ["Field 'asset' must be an asset object"], "warnings": [] } • HTTP Status Code: 400 {"errors": ["Field 'asset' missing from scan"], "warnings": [] } • HTTP Status Code: 400 {"errors": ["Malformed IP address '192.168.a.1'"], "warnings": [] } If the asset does not exist in the given namespace the following response will be encountered: - HTTP Status Code: 403 {"errors": ["Unauthorized", "Asset 98:be:94:f9:d2:2b not found for user eff52988-f059-363d-b333-7464af942b70"]} List of valid asset categories Returns the current list of valid asset categories. GET https://ccom2-api.abedgraham.com/_/api/:user_uuid/asset_categories When submitting a scan, the category field of the asset must be one of these possible categories listed in this response. Only exact string matches are allowed. Where: • user_uuid the UUID of user. Example responses: • HTTP Status Code: 200 {"asset_categories": ["X-Ray Machine", "DICOM Imager", "Workstation" ] }