GraphQL API Documentation

GraphQL API Documentation

This document is intended to be a simple introduction to our GraphQL API, and (in this current alpha phase) explicitly definining a particular subset which can be considered officially suupported. Ultimately the entire graph should be considered “supported” unless otherwise annotated (e.g. with @deprecation or @experimental warnings).

The base url of our graph API will change in the near future to api.nowsecure.com, though we will continue to support GraphQL API calls to lab-api.nowsecure.com/graphql in the near term.

Using the Graph API

There are many ways to consume the GraphQL API, and there are quite a few good resources online detailing some of the possible ways to interface with a Graph, such as this guide. There are also a variety of GraphQL client libraries in any languages that can leverage a GraphQL API advertised service and type definitions. While all of this helps aid explorability of the API, but when it comes to consuming data, a simple HTTP POST request is all you’ll need.

HTTP POST

Interacting with the Graph API from code, e.g. through curl or via HTTP client library, is as simple as an HTTP POST with the relevant GraphQL query.

Here’s an simple example query to list out NowSecure’s findings, resolving just the finding id and title for each:

curl \
  -X POST \
  -H "Authorization: Bearer ${AUTH_TOKEN}" \
  -H "Content-Type: application/json" \
  --data '{ "query": "{ findings { id title } }" }' \
  https://lab-api.nowsecure.com/graphql

The results will come back as a JSON with a data key. The values will be structured as requested (with result set formatted and elided for clarity):

{
  "data": {
    "findings": [
      {
        "id": "asl",
        "title": "System Log Messages (ASL)"
      },
      {
        "id": "oslog",
        "title": "System Log Messages (OSLog)"
      },
      {
        "id": "geoip",
        "title": "Network Connections"
      },
      {
        "id": "snoop_network_hosts",
        "title": "Network Connections"
      },
      // ...
    ]
  }
}

GraphQL Explorer

The GraphQL explorer – available at https://lab-api.nowsecure.com/graphiql – is a web app for browsing and interacting with NowSecure’s GraphQL API. This is a standard tool within the graphql toolbox, and makes learning, browsing and prodding at any given GraphQL API much more accessible, even to non-developers. Among many other features, it provides auto-completion when authoring queries, syntax highlighting and auto-formatting of queries and results, and a powerful, searchable Documentation Explorer panel. This allows the entire API to be explored one type at a time. Any documentation available for a given type, and/or for any function arguments or record attributes, are surfaced right inline.

Authentication

In order to authenticate, you must also provide a valid token parameter in the querystring – specifing the same JWT token you would use to interact with the API through any other means. For example, this might look like: https://lab-api.nowsecure.com/graphiql?token=``.

Pre-release Supported Subset

This is preliminary, pre-release documentation of our GraphQL API. While there is a variety of features accessible via the GraphQL API, and reachable from within the explorer, the bulk of the graph is still somewhat in flux, as it has grown somewhat organically and is not yet internally self-consistent. The query patterns documented below of graph nodes available from within the explorer, the API itself is still in flux

Alerts

To query for alerts for the authenticated user, you would use the myAlerts query node. For example, to query for all unread alerts, sorted such that most recent alerts come first, it might look something like this:

query {
  alerts {
    myAlerts(isRead: false, sortDescending: true) {
      id
      eventType
      createdAt
      readAt
    }
  }
}

The GraphQL JSON response:

{
  "data": {
    "alerts": {
      "myAlerts": [
        {
          "id": 6172,
          "eventType": "ASSESSMENT_DONE",
          "createdAt": "2019-02-08T19:11:21.109Z",
          "readAt": true
        },
        {
          "id": 6171,
          "eventType": "ASSESSMENT_DONE",
          "createdAt": "2019-02-08T16:10:56.258Z",
          "readAt": true
        }
      ]
    }
  }
}

The myAlerts query node takes two arguments: the isRead parameter allows filtering strictly for alerts which have been either read or unread (if unspecified, both read and unread alerts will be returend). The sortDescending parameter allows for fetching alerts by most recent first, rather than the default chronological order (the order alerts were created in).

We will likely rework how we surface the sort specification (e.g. we may define an explicit enumeration of available sorts, allowing us to support whatever can be efficiently serviced from available indexes). Regardless, we can maintain backward compatibility, and continue to support this particular sortDescending argument indefinitely.

In the near future this likely be annotated with a deprecation warning, with instructions about what to use instead to get semantically equivalent behavior.

Change History Audit Logs

The audit > changes node can be queried to see all changes on audited attributes for all entities across the org. (For the time being, this feature requires org admin privileges – as org admins inherently have access to all of this data via existing APIs – but as this feature solidifies we will be able to integrate it with existing RBAC mechanisms.)

For example, to resolve all information for every historical change across the org:

query {
  audit {
    changes {
      entityType
      entityRef
      attribute
      value
      updatedAt
      updatedBy
    }
  }
}

This result set can be sliced by timeframe using the optional since and until arguments, and an integer limit argument is also available. The results may also be filtered for specific entityTypes, and/or for a given set of attributes. For example, to see all cvss edits this year:

query {
  audit {
    changes(since: "2019-01-01T00:00:00Z", attributes: ["edit:cvss"]) {
      value
      updatedAt
      updatedBy
    }
  }
}

Due to limitations of the graphql DateTime type we require a full ISO 8601 timestamp for the time being.

Assuming change events exist in the date range, the result set should look something like this:

{
  "data": {
    "audit": {
      "changes": [
        {
          "value": 4.5,
          "updatedAt": "2019-01-16T23:40:28.611Z",
          "updatedBy": "81e3b350-0325-457e-8d32-f7202ce8a2a8"
        },
        {
          "value": 5.4,
          "updatedAt": "2019-01-22T17:26:58.562Z",
          "updatedBy": "81e3b350-0325-457e-8d32-f7202ce8a2a8"
        },
        // ...
      ]
    }
  }
}

There are still some change events where the user responsible for the change is not being logged. These gaps will be remedied in subsequent releases.

Longer-term deprecation policy

In the long term – as the graph API structure stabilizes – any graph nodes we would like to remove will be annotated with a deprecation, which should include instructions on where best to query for the relevant data instead. Using deprecated nodes in a query will include these warnings. While it is not adviseable to develop new application logic against deprecated nodes, we will not remove any deprecated nodes/functionality without a great deal of advanced warning (we have historically been extremely conservative about preserving backward-compatibility, warts and all – and should remain so, never outright disabling any deprecated functionality which logs show are still in active use).