Skip to main content

README.md

# Intel471Ex

An Elixir client library for Intel 471's Verity API providing access to cyber threat intelligence.

## Table of Contents

- [Installation](#installation)
- [Configuration](#configuration)
- [API Coverage](#api-coverage)
- [API Examples](#api-examples)
  - [Actors](#actors)
  - [Reports](#reports)
  - [Credentials](#credentials)
  - [Watchers](#watchers)
  - [Entities](#entities)
  - [Indicators](#indicators)
  - [Malware Intel](#malware-intel)
  - [Observables](#observables)
  - [GIRS](#girs)
  - [Sources](#sources)
  - [ASE](#ase)
  - [Brand Exposure](#brand-exposure)
  - [TPRM](#tprm)
- [File Downloads](#file-downloads)
- [Error Handling](#error-handling)

## Installation

Add `intel471_ex` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:intel471_ex, "~> 0.2.0"}
  ]
end
```

## Configuration

This library uses system environment variables for authentication. Create an application in the [Verity Developer Portal](https://verity.intel471.com) to obtain a client ID and client secret:

```
# Required
VERITY_CLIENT_ID=your-verity-client-id
VERITY_CLIENT_SECRET=your-verity-client-secret

# Optional
VERITY_API_URL=https://api.intel471.cloud  # Default API URL
```

Set them in your application:

```elixir
System.put_env("VERITY_CLIENT_ID", "your-client-id")
System.put_env("VERITY_CLIENT_SECRET", "your-client-secret")
```

## API Coverage

| Service | Module | Key Functions |
|---------|--------|--------------|
| Actors | `Intel471Ex.Actors` | `stream` |
| ASE | `Intel471Ex.Ase` | `list_monitors` |
| Brand Exposure | `Intel471Ex.BrandExposure` | Monitor CRUD, scan data, config |
| Credentials | `Intel471Ex.Credentials` | Credential/set/occurrence streams and lookups |
| Entities | `Intel471Ex.Entities` | `stream` |
| GIRS | `Intel471Ex.Girs` | `tree` |
| Indicators | `Intel471Ex.Indicators` | `stream`, `get_by_id` |
| Malware Intel | `Intel471Ex.Malware` | `events_stream`, `list_malware`, `get_malware_family`, `download_file` |
| Observables | `Intel471Ex.Observables` | `stream` |
| Reports | `Intel471Ex.Reports` | Stream/detail for FINTel, Breach Alert, Geopol, Info, Spot, Malware, Vulnerability |
| Sources | `Intel471Ex.Sources` | Stream/detail for data-leak-site posts, forum posts/messages, chat messages |
| TPRM | `Intel471Ex.Tprm` | Monitor CRUD, scan data, config |
| Watchers | `Intel471Ex.Watchers` | `alerts_stream`, `update_alert_status`, `watcher_groups`, `watchers` |

## API Examples

### Actors

```elixir
# Stream actors matching a search term
{:ok, result} = Intel471Ex.Actors.stream(%{actor: "lazarus", size: 10})
IO.puts("Found #{result["count"]} actors")

# Stream with pagination
{:ok, page1} = Intel471Ex.Actors.stream(%{actor: "conti", size: 5})
cursor = page1["cursor_next"]
{:ok, page2} = Intel471Ex.Actors.stream(%{actor: "conti", size: 5, cursor: cursor})

# Filter by server type
{:ok, result} = Intel471Ex.Actors.stream(%{actor: "conti", server_type: "telegram"})
```

### Reports

```elixir
# Stream all report types
{:ok, result} = Intel471Ex.Reports.stream(%{text_filter: "ransomware", size: 10})

# FINTel reports
{:ok, result} = Intel471Ex.Reports.fintel_stream(%{text_filter: "malware"})
{:ok, report} = Intel471Ex.Reports.fintel_detail("report-id")

# Breach alerts
{:ok, result} = Intel471Ex.Reports.breach_alert_stream(%{text_filter: "Communications"})
{:ok, alert} = Intel471Ex.Reports.breach_alert_detail("alert-id")

# Geopolitical reports
{:ok, result} = Intel471Ex.Reports.geopol_stream(%{country: "US"})
{:ok, report} = Intel471Ex.Reports.geopol_detail("report-id")

# Info reports
{:ok, result} = Intel471Ex.Reports.info_stream(%{text_filter: "vulnerability"})

# Malware reports
{:ok, result} = Intel471Ex.Reports.malware_stream(%{malware_family: "lokibot"})

# Spot reports
{:ok, result} = Intel471Ex.Reports.spot_stream(%{text_filter: "phishing"})

# Vulnerability reports
{:ok, result} = Intel471Ex.Reports.vulnerability_stream(%{cve_name: "CVE-2024-1234"})
{:ok, vuln} = Intel471Ex.Reports.vulnerability_detail("vuln-id")

# Download vulnerability report as PDF
{:ok, %{body: pdf_data}} = Intel471Ex.Reports.vulnerability_download_pdf("vuln-id")
```

### Credentials

```elixir
# Stream credentials
{:ok, result} = Intel471Ex.Credentials.credential_stream(%{domain: "example.com", size: 10})
{:ok, credential} = Intel471Ex.Credentials.get_credential("credential-id")

# Credential occurrences
{:ok, result} = Intel471Ex.Credentials.credential_occurrence_stream(%{credential_id: "id"})
{:ok, occurrence} = Intel471Ex.Credentials.get_credential_occurrence("occurrence-id")

# Credential sets
{:ok, result} = Intel471Ex.Credentials.credential_set_stream(%{victim: "example.com"})
{:ok, set} = Intel471Ex.Credentials.get_credential_set("set-id")

# Accessed URLs
{:ok, result} = Intel471Ex.Credentials.credential_set_accessed_url_stream(%{credential_set_id: "id"})
```

### Watchers

```elixir
# Stream alerts
{:ok, result} = Intel471Ex.Watchers.alerts_stream(%{size: 10})

# Update alert status
{:ok, _} = Intel471Ex.Watchers.update_alert_status(12345, "read")

# List watcher groups
{:ok, result} = Intel471Ex.Watchers.watcher_groups()

# List watchers
{:ok, result} = Intel471Ex.Watchers.watchers(%{watcher_group_id: "group-id"})
```

### Entities

```elixir
# Stream entities
{:ok, result} = Intel471Ex.Entities.stream(%{entity: "intel.com", size: 10})
```

### Indicators

```elixir
# Stream indicators
{:ok, result} = Intel471Ex.Indicators.stream(%{type: "domain", size: 10})

# Get indicator by ID
{:ok, indicator} = Intel471Ex.Indicators.get_by_id("indicator-id")
```

### Malware Intel

```elixir
# Stream malware events
{:ok, result} = Intel471Ex.Malware.events_stream(%{malware_family_name: "lokibot", size: 10})
{:ok, event} = Intel471Ex.Malware.get_event("event-id")

# List malware families
{:ok, result} = Intel471Ex.Malware.list_malware(%{text_filter: "trickbot"})

# Get malware family detail
{:ok, family} = Intel471Ex.Malware.get_malware_family("family-id")

# Download malware file
{:ok, %{body: file_data}} = Intel471Ex.Malware.download_file("filename.exe")
```

### Observables

```elixir
# Stream observables
{:ok, result} = Intel471Ex.Observables.stream(%{observable: "8.8.8.8", size: 10})
```

### GIRS

```elixir
# Get the GIRS tree
{:ok, tree} = Intel471Ex.Girs.tree()
IO.puts("Found #{tree["count"]} GIR entries")
```

### Sources

```elixir
# Data leak site posts
{:ok, result} = Intel471Ex.Sources.data_leak_site_posts_stream(%{text_filter: "ransomware"})

# Forum posts
{:ok, result} = Intel471Ex.Sources.forums_posts_stream(%{author: "username"})
{:ok, post} = Intel471Ex.Sources.get_forum_post("post-id")

# Forum private messages
{:ok, result} = Intel471Ex.Sources.forums_private_messages_stream(%{author: "username"})

# Chat messages
{:ok, result} = Intel471Ex.Sources.chat_messages_stream(%{server_type: "telegram"})

# Get an image
{:ok, %{body: image_data}} = Intel471Ex.Sources.get_image("forum", "hash", "image.png")
```

### ASE

```elixir
# List ASE monitors
{:ok, monitors} = Intel471Ex.Ase.list_monitors()
```

### Brand Exposure

```elixir
# Monitor CRUD
{:ok, monitors} = Intel471Ex.BrandExposure.list_monitors()
{:ok, monitor} = Intel471Ex.BrandExposure.create_monitor(%{
  name: "example.com",
  targets: ["example.com"],
  labels: ["brand-monitor"],
  frequency: "daily",
  impact: "major"
})
{:ok, monitor} = Intel471Ex.BrandExposure.get_monitor("monitor-id")
{:ok, _} = Intel471Ex.BrandExposure.delete_monitor("monitor-id")

# Configuration
{:ok, config} = Intel471Ex.BrandExposure.get_config_current("base")
{:ok, schema} = Intel471Ex.BrandExposure.get_config_schema()
```

### TPRM

```elixir
# Monitor CRUD
{:ok, monitors} = Intel471Ex.Tprm.list_monitors()
{:ok, monitor} = Intel471Ex.Tprm.create_monitor(%{
  name: "vendor.com",
  targets: ["vendor.com"],
  collection_method: "passive"
})

# Configuration (uses Bearer token for writes)
{:ok, config} = Intel471Ex.Tprm.get_config_current("base")
{:ok, _} = Intel471Ex.Tprm.put_config_user("base", %{"key" => "value"}, "bearer-token")
```

## File Downloads

```elixir
# Download a file to a specific path
{:ok, path} = Intel471Ex.Downloader.download_file(
  "https://api.intel471.cloud/integrations/malware-intel/v1/malware/files/sample/download",
  "/tmp/malware_sample.zip"
)

# Auto-extract filename from URL
{:ok, path} = Intel471Ex.Downloader.download_file_auto(
  "https://api.intel471.cloud/integrations/sources/v1/data-leak-sites/file-listings/123",
  "downloads"
)
```

## Error Handling

```elixir
case Intel471Ex.Actors.stream(%{actor: "test"}) do
  {:ok, result} ->
    IO.puts("Found #{result["count"]} actors")

  {:error, %{status: status, message: message}} ->
    IO.puts("API error #{status}: #{message}")

  {:error, exception} ->
    IO.puts("Network error: #{inspect(exception)}")
end
```

## Migration from v0.1.x (Titan API)

This version (0.2.0) is a breaking change. Key migration steps:

1. **Environment variables**: Replace `INTEL471_USERNAME`/`INTEL471_API_KEY` with `VERITY_CLIENT_ID`/`VERITY_CLIENT_SECRET`
2. **API URL**: Default changed from `https://api.intel471.com/v1` to `https://api.intel471.cloud`
3. **Removed modules**: `Intel471Ex.Alerts`, `Intel471Ex.Search`, `Intel471Ex.Vulnerabilities`
4. **New modules**: `Entities`, `Indicators`, `Malware`, `Observables`, `Girs`, `Sources`, `Ase`, `BrandExposure`, `Tprm`
5. **API style**: All endpoints now use cursor-based streaming instead of offset/count pagination