Guardsix

[!NOTE] This package was previously published as logpoint_api and has been renamed to guardsix following the company rebranding.

A stateless implementation of the Guardsix SIEM API Reference. The library is a wrapper around the API reference with the addition of builder patterns for alert rules and notifications. I try to make sure the library stays as true to the API as possible with minor simplifications so it is easier to correlate the lib with the API reference doc.

Installation

def deps do
  [
    {:guardsix, "~> 1.0.0"}
  ]
end

Basic Usage

Create a universal client that can be used with all functions. This is a simplification over the split in the API design where some endpoints use JWT tokens and others don't.

client = Guardsix.client("https://guardsix.company.com", "admin", "your_secret_key")

Search

Search includes all functions from the search API reference and for writing search_param queries please refer to Search Log Data.

alias Guardsix.Core.Search

query = Guardsix.search_params(
  "user=*",
  "Last 24 hours",
  100,
  ["127.0.0.1:5504"]
)

{:ok, %{"search_id" => search_id}} = Search.get_id(client, query)
{:ok, result} = Search.get_result(client, search_id)

Instance Information

alias Guardsix.Core.Search

{:ok, user_prefs} = Search.user_preference(client)
{:ok, repos}      = Search.repos(client)
{:ok, devices}    = Search.devices(client)

Incident Management

The incident module wraps the Incident API.

alias Guardsix.Core.Incident

# List incidents within a time range
{:ok, incidents} = Incident.list(client, 1_714_986_600, 1_715_031_000)
{:ok, states}    = Incident.list_states(client, 1_714_986_600, 1_715_031_000)

# Get a specific incident where both the incident_obj_id and incident_id
# is needed to get the unique incident.
{:ok, incident} = Incident.get(client, "incident_obj_id", "incident_id")

# Add comments
comments = [Guardsix.comment("incident_id_1", "This needs attention")]
{:ok, _} = Incident.add_comments(client, comments)

# Assign and update states
{:ok, _} = Incident.assign(client, ["incident_id_1"], "user_id")
{:ok, _} = Incident.resolve(client, ["incident_id_1"])
{:ok, _} = Incident.close(client, ["incident_id_2"])
{:ok, _} = Incident.reopen(client, ["incident_id_3"])

# Get users
{:ok, users} = Incident.get_users(client)

Alert Rules

AlertRule wraps the Alert Rules API. All parameters for alert rule creation are defined but please refer to the alert rule builder for a composable structure for building rules.

alias Guardsix.Core.AlertRule

{:ok, rules} = AlertRule.list(client)
{:ok, rule}  = AlertRule.get(client, "rule-id")
{:ok, _}     = AlertRule.activate(client, ["id1", "id2"])
{:ok, _}     = AlertRule.deactivate(client, ["id1"])
{:ok, _}     = AlertRule.delete(client, ["id1"])
{:ok, notif} = AlertRule.get_notification(client, "rule-id", :email)

Alert Rule Builder

Compose alert rules to be used with the create alert rule endpoint.

alias Guardsix.Data.Rule

rule =
  Guardsix.rule("Brute Force Detection")
  |> Rule.description("Detects brute force login attempts")
  |> Rule.query("error_code=4625")
  |> Rule.time_range(1, :day)
  |> Rule.repos(["10.0.0.1"])
  |> Rule.limit(100)
  |> Rule.threshold(:greaterthan, 5)
  |> Rule.risk_level("high")
  |> Rule.mitre_tags(["T1110"])

{:ok, _} = AlertRule.create(client, rule)

Notification Builders

Compose notifications for alert rules.

alias Guardsix.Data.EmailNotification
alias Guardsix.Data.HttpNotification

# Email notification
notif =
  Guardsix.email_notification(["rule-1"], "admin@example.com")
  |> EmailNotification.subject("Alert: {{ rule_name }}")
  |> EmailNotification.template("<p>Details</p>")

{:ok, _} = AlertRule.create_email_notification(client, notif)

# HTTP notification with bearer auth
webhook =
  Guardsix.http_notification(["rule-1"], "https://hooks.slack.com/abc", :post)
  |> HttpNotification.body(~s({"text": "{{ rule_name }}"}))
  |> HttpNotification.bearer_auth("my-token")

{:ok, _} = AlertRule.create_http_notification(client, webhook)

# Other auth types: no_auth/1, api_token_auth/3, basic_auth/3
webhook
|> HttpNotification.api_token_auth("X-API-Key", "secret123")
|> HttpNotification.basic_auth("user", "pass")

Guardsix Repos and User-Defined Lists

alias Guardsix.Core.GuardsixRepo
alias Guardsix.Core.UserDefinedList

{:ok, repos} = GuardsixRepo.list(client)
{:ok, lists} = UserDefinedList.list(client)

SSL Configuration

Pass ssl_verify: false to disable SSL verification (e.g. for self-signed certificates):

client = Guardsix.client("https://192.168.1.100", "admin", "your_secret_key", ssl_verify: false)

Error Handling

All functions return {:ok, result} or {:error, reason} tuples:

alias Guardsix.Core.Search

case Search.get_id(client, query) do
  {:ok, %{"search_id" => search_id}} ->
    IO.puts("Search started: #{search_id}")

  {:error, reason} ->
    IO.puts("Search failed: #{inspect(reason)}")
end

Contributing

Please refer to CONTRIBUTING.md for development guidelines.

License

This project is licensed under the MIT License - see the LICENSE file for details.