TheHive API Client for Rust

May 24, 2025 ยท View on GitHub

TheHive is an open-source, scalable, and collaborative Security Incident Response Platform (SIRP). It is designed to help SOCs, CSIRTs, CERTs, and any security practitioner to investigate and respond to security incidents quickly and efficiently. TheHive allows analysts to create cases for investigations, add observables (like IPs, URLs, domains, files), create tasks, log their progress, and collaborate with team members.

The TheHive API provides a programmatic interface to its comprehensive set of features, offering several advantages:

  • Automation: Automate the creation of alerts and cases from external systems (SIEMs, EDRs, email alerts), and manage their lifecycle programmatically.
  • Integration: Seamlessly integrate TheHive's incident management capabilities into your existing security ecosystem (SOAR platforms, threat intelligence feeds, custom scripts).
  • Scalability: Handle a large volume of security events and manage numerous cases and observables programmatically.
  • Customization: Build custom scripts and tools that leverage TheHive's backend for tailored security operations and reporting.
  • Efficiency: Speed up incident response by automating repetitive tasks, enriching cases with threat intelligence, and orchestrating response actions.

Installation

To use this client in your Rust project, add it as a dependency in your Cargo.toml.

[dependencies]
thehive-client = "0.1"

Rust Usage Examples

Below are conceptual examples of how to use the generated Rust client to interact with TheHive API. Ensure you have configured your client with the correct TheHive URL and API key, via environment variables THEHIVE_API_ENDPOINT and THEHIVE_API_KEY.

1. Create an Alert with an IP Observable

This example demonstrates how to create a new alert in TheHive and attach an IP address as an observable.

use std::env;
use thehive_client::apis::configuration::Configuration;
use thehive_client::apis::alert_api; // Module for alert operations
use thehive_client::models::{InputAlert, InputObservable};

async fn create_alert_with_ip(
    thehive_url: &str,
    api_key: &str,
    alert_title: &str,
    ip_address: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let mut config = Configuration::new();
    config.base_path = thehive_url.to_string();
    config.bearer_access_token = Some(api_key.to_string());

    let observable = InputObservable {
        data_type: "ip".to_string(),
        data: Some(ip_address.to_string()),
        message: Some("Suspicious IP address from Rust example".to_string()),
        ioc: Some(true),
        tlp: Some(2), // TLP:AMBER (0:White, 1:Green, 2:Amber, 3:Red)
        pap: Some(2), // PAP:AMBER (0:White, 1:Green, 2:Amber, 3:Red)
        tags: Some(vec!["suspicious_ip".to_string()]),
        ..Default::default() // Fills other Option fields with None
    };

    let alert_data = InputAlert {
        r#type: "external_event".to_string(), // A common type for alerts
        source: "rust_client_example_source".to_string(),
        source_ref: format!("rust-example-alert-{}", chrono::Utc::now().timestamp_micros()), // Example unique ref
        title: alert_title.to_string(),
        description: format!("This alert was automatically generated by the Rust client regarding IP: {}.", ip_address),
        severity: Some(2), // Severity: 1 (Low), 2 (Medium), 3 (High), 4 (Critical)
        tags: Some(vec!["rust_example".to_string(), "auto_generated_alert".to_string()]),
        observables: Some(vec![observable]),
        ..Default::default()
    };

    match alert_api::create_alert(&config, alert_data, None).await {
        Ok(created_alert) => {
            println!(
                "Alert '{}' created successfully! ID: {}",
                created_alert.title,
                created_alert._id
            );
            // You can now use created_alert._id for further operations like promoting to case.
        }
        Err(e) => {
            eprintln!("Error creating alert: {:?}", e);
        }
    }
    Ok(())
}

2. Create a New Case

This example shows how to create a new case from scratch.

use std::env;
use thehive_client::apis::configuration::Configuration;
use thehive_client::apis::case_api; // Module for case operations
use thehive_client::models::{InputCase, CaseStatusValue};

async fn create_new_case(
    thehive_url: &str,
    api_key: &str,
    case_title: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let mut config = Configuration::new();
    config.base_path = thehive_url.to_string();
    config.bearer_access_token = Some(api_key.to_string());

    let case_data = InputCase {
        title: case_title.to_string(),
        description: "This is a new case created via the Rust API client.".to_string(),
        severity: Some(2), // Medium
        tags: Some(vec!["rust_example".to_string(), "new_investigation".to_string()]),
        flag: Some(false),
        tlp: Some(2), // TLP:AMBER
        pap: Some(2), // PAP:AMBER
        status: Some(CaseStatusValue::New), 
        ..Default::default()
    };

    match case_api::create_case(&config, case_data, None).await {
        Ok(created_case) => {
            println!(
                "Case '{}' created successfully! ID: {}, Number: {}",
                created_case.title,
                created_case._id,
                created_case.number
            );
            // You can now use created_case._id or created_case.number for further operations.
        }
        Err(e) => {
            eprintln!("Error creating case: {:?}", e);
        }
    }
    Ok(())
}

3. Promote an Alert to a Case

This example demonstrates how to promote an existing alert to a new case. You would get the alert_id_to_promote from a previous operation (like creating an alert).

use std::env;
use thehive_client::apis::configuration::Configuration;
use thehive_client::apis::alert_api; 
use thehive_client::models::InputPromoteAlert; 

async fn promote_alert_to_new_case(
    thehive_url: &str,
    api_key: &str,
    alert_id_to_promote: &str,
    case_title_prefix: Option<String>,
) -> Result<(), Box<dyn std::error::Error>> {
    let mut config = Configuration::new();
    config.base_path = thehive_url.to_string();
    config.bearer_access_token = Some(api_key.to_string());

    let promotion_details = InputPromoteAlert {
        title: case_title_prefix, 
        ..Default::default()
    };

    match alert_api::promote_alert_to_case(&config, alert_id_to_promote, None, Some(promotion_details)).await {
        Ok(created_case) => {
            println!(
                "Alert {} promoted to Case '{}' successfully! Case ID: {}, Case Number: {}",
                alert_id_to_promote,
                created_case.title,
                created_case._id,
                created_case.number
            );
        }
        Err(e) => {
            eprintln!("Error promoting alert {} to case: {:?}", alert_id_to_promote, e);
        }
    }
    Ok(())
}

Running the Examples

To run the provided examples, you'll first need to ensure you have a running TheHive instance and the necessary credentials. The examples are configured to use environment variables for the API endpoint and API key.

Prerequisites:

  • A running TheHive 5.x instance.
  • An API key for your TheHive user.
  • Rust and Cargo installed.
  • The client library cloned or added as a dependency to your project as described in the "Installation" section.

Environment Variables: Before running any example, you need to set the following environment variables in your terminal session:

  • THEHIVE_API_ENDPOINT: The URL of your TheHive API. If not set, it defaults to http://localhost:9000/api in the examples.
    export THEHIVE_API_ENDPOINT="http://your-thehive-instance.com:9000/api"
    
  • THEHIVE_API_KEY: Your TheHive API key. This variable must be set.
    export THEHIVE_API_KEY="your_actual_api_key"
    

Executing an Example: Once the environment variables are set, you can run an example using Cargo. The examples are located in the examples/ directory and defined in the Cargo.toml file.

For instance, to run an example named create_minimalistic_case:

cargo run --example create_minimalistic_case

Replace create_minimalistic_case with the actual name of the example you wish to run. You can list available examples by checking the examples/ directory and the Cargo.toml file.

Documentation for API Endpoints

All URIs are relative to http://localhost:9000/api

ClassMethodHTTP requestDescription
AlertApiadd_alert_attachmentsPOST /v1/alert/{alert_id}/attachmentsAdd attachments to an alert
AlertApibulk_delete_alertsPOST /v1/alert/delete/_bulkBulk delete alerts
AlertApibulk_merge_alerts_into_casePOST /v1/alert/merge/_bulkBulk merge alerts into a case
AlertApibulk_update_alertsPATCH /v1/alert/_bulkBulk update alerts
AlertApicreate_alertPOST /v1/alertCreate an alert
AlertApicreate_alert_observablePOST /v1/alert/{alert_id}/observableCreate an observable in an alert
AlertApicreate_alert_procedurePOST /v1/alert/{alert_id}/procedureCreate a procedure in an alert
AlertApidelete_alertDELETE /v1/alert/{alert_id}Delete an alert
AlertApidelete_alert_attachmentDELETE /v1/alert/{alert_id}/attachment/{attachment_id}Delete an alert attachment
AlertApidownload_alert_attachmentGET /v1/alert/{alert_id}/attachment/{attachment_id}/downloadDownload an alert attachment
AlertApifollow_alertPOST /v1/alert/{alert_id}/followFollow an alert
AlertApiget_alert_by_idGET /v1/alert/{alert_id}Get an alert by ID
AlertApimerge_alert_into_casePOST /v1/alert/{alert_id}/merge/{case_id}Merge an alert into an existing case
AlertApipromote_alert_to_casePOST /v1/alert/{alert_id}/casePromote an alert to a case
AlertApiunfollow_alertPOST /v1/alert/{alert_id}/unfollowUnfollow an alert
AlertApiupdate_alertPATCH /v1/alert/{alert_id}Update an alert
CaseApicreate_casePOST /v1/caseCreate a case
CaseApidelete_caseDELETE /v1/case/{case_id}Delete a case
CaseApiget_case_by_idGET /v1/case/{case_id}Get a case by ID or number
CaseApiupdate_casePATCH /v1/case/{case_id}Update a case
CaseTemplateApicreate_case_templatePOST /v1/caseTemplateCreate a case template
CaseTemplateApidelete_case_templateDELETE /v1/caseTemplate/{case_template_id}Delete a case template
CaseTemplateApiget_case_template_by_idGET /v1/caseTemplate/{case_template_id}Get a case template by ID
CaseTemplateApiupdate_case_templatePATCH /v1/caseTemplate/{case_template_id}Update a case template
CommentApicreate_alert_commentPOST /v1/alert/{alert_id}/commentCreate a comment in an alert
CommentApicreate_case_commentPOST /v1/case/{case_id}/commentCreate a comment in a case
CommentApidelete_commentDELETE /v1/comment/{comment_id}Delete a comment
CommentApiupdate_commentPATCH /v1/comment/{comment_id}Update a comment
QueryApifind_entities_by_queryPOST /v1/queryFind entities using a flexible query
UserApicreate_userPOST /v1/userCreate a new user
UserApidelete_userDELETE /v1/user/{user_id}/forceDelete a user (force)
UserApiget_current_userGET /v1/user/currentGet current authenticated user's details
UserApiget_user_api_keyGET /v1/user/{user_id}/keyGet user's API key
UserApiget_user_by_idGET /v1/user/{user_id}Get user details by ID
UserApiremove_user_api_keyDELETE /v1/user/{user_id}/keyRemove user's API key
UserApirenew_user_api_keyPOST /v1/user/{user_id}/key/renewRenew user's API key
UserApiset_user_organisationsPUT /v1/user/{user_id}/organisationsSet user's organisations and profiles
UserApiset_user_passwordPOST /v1/user/{user_id}/password/setSet a user's password
UserApiupdate_userPATCH /v1/user/{user_id}Update user details

Documentation For Models