Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions internal-enrichment/xposedornot/.build.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CONNECTOR_CMD="-m src"
CONNECTOR_WORKDIR="/opt/connector"
10 changes: 10 additions & 0 deletions internal-enrichment/xposedornot/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
**/logs
**/*.gql
**/venv
**/.venv
**/__pycache__/
**/*.egg-info/
**/config.yml
**/__pycache__
**/__metadata__
**/__docs__
13 changes: 13 additions & 0 deletions internal-enrichment/xposedornot/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
OPENCTI_URL=http://opencti:8080
OPENCTI_TOKEN=ChangeMe
CONNECTOR_ID=ChangeMe
CONNECTOR_TYPE=INTERNAL_ENRICHMENT
CONNECTOR_NAME=XposedOrNot
CONNECTOR_SCOPE=Email-Addr
CONNECTOR_LOG_LEVEL=info
CONNECTOR_AUTO=false
# Optional; leave unset to use the free keyless community API
XPOSEDORNOT_API_KEY=
XPOSEDORNOT_API_BASE_URL=https://api.xposedornot.com
XPOSEDORNOT_MAX_TLP=TLP:AMBER
XPOSEDORNOT_TLP_LEVEL=amber
8 changes: 8 additions & 0 deletions internal-enrichment/xposedornot/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
__pycache__/
*.py[cod]
*.egg-info/
.pytest_cache/
.venv/
venv/
config.yml
.env
16 changes: 16 additions & 0 deletions internal-enrichment/xposedornot/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM python:3.12-alpine
ENV CONNECTOR_TYPE=INTERNAL_ENRICHMENT

# Copy the connector
COPY src /opt/connector/src

WORKDIR /opt/connector

# Install Python modules
# hadolint ignore=DL3003
RUN apk --no-cache add git build-base libmagic libffi-dev && \
cd /opt/connector/src && \
pip3 install --no-cache-dir -r /opt/connector/src/requirements.txt && \
apk del git build-base

CMD ["python", "-m", "src"]
96 changes: 96 additions & 0 deletions internal-enrichment/xposedornot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# OpenCTI XposedOrNot Connector

<!--
General overview of the connector:
* What it does: enrich Email-Addr observables with data-breach exposure from XposedOrNot
* Works without any API key (free community API); optional key raises rate limits
-->

Table of Contents

- [OpenCTI XposedOrNot Connector](#opencti-xposedornot-connector)
- [Introduction](#introduction)
- [Installation](#installation)
- [Requirements](#requirements)
- [Configuration variables](#configuration-variables)
- [Deployment](#deployment)
- [Docker Deployment](#docker-deployment)
- [Manual Deployment](#manual-deployment)
- [Usage](#usage)
- [Behavior](#behavior)
- [Legal and privacy notice](#legal-and-privacy-notice)
- [Debugging](#debugging)

## Introduction

[XposedOrNot](https://xposedornot.com) is an open, free data-breach search service tracking 760+ known breaches. Given an email address it returns the breaches the address appears in, with per-breach detail: breach date, records exposed, exposed data classes, affected domain, industry and password-storage risk, plus an overall risk score.

This internal-enrichment connector enriches `Email-Addr` observables with that exposure data. **No API key or registration is required** — the free community API is used by default. An optional commercial key ([console.xposedornot.com](https://console.xposedornot.com)) switches the connector to the Plus API with higher rate limits.

## Installation

### Requirements

- OpenCTI Platform >= 6.8.12
- No XposedOrNot account or API key needed (optional key for higher volume)

## Configuration variables

Full configuration reference: [`__metadata__/CONNECTOR_CONFIG_DOC.md`](__metadata__/CONNECTOR_CONFIG_DOC.md).

Key parameters:

| Parameter | Docker env var | Mandatory | Default | Description |
|-----------|----------------|-----------|---------|-------------|
| OpenCTI URL | `OPENCTI_URL` | Yes | | The OpenCTI platform URL. |
| OpenCTI Token | `OPENCTI_TOKEN` | Yes | | Token of the connector user. |
| Connector Auto | `CONNECTOR_AUTO` | No | `false` | Automatic enrichment. The keyless API allows 2 req/s and 25/hour per IP — keep manual, or configure an API key before enabling on busy platforms. |
| API key | `XPOSEDORNOT_API_KEY` | No | *(empty)* | Optional key; switches to the Plus API with higher limits. Fully functional without it. |
| Base URL | `XPOSEDORNOT_API_BASE_URL` | No | `https://api.xposedornot.com` | Free community API base URL. |
| Max TLP | `XPOSEDORNOT_MAX_TLP` | No | `TLP:AMBER` | Maximum TLP of an observable the connector may enrich (the email address is sent to the API). |
| TLP level | `XPOSEDORNOT_TLP_LEVEL` | No | `amber` | TLP marking applied to the produced objects. |

## Deployment

### Docker Deployment

Use the provided `docker-compose.yml` (or add the service to your OpenCTI stack):

```shell
docker compose up -d
```

### Manual Deployment

```shell
# From the connector root directory (internal-enrichment/xposedornot):
pip3 install -r src/requirements.txt
cp config.yml.sample config.yml # then edit config.yml
python3 -m src
```

## Usage

On an `Email-Addr` observable, click the enrichment button and select the XposedOrNot connector (or set `CONNECTOR_AUTO=true` — mind the rate limits above). The connector is playbook-compatible.

## Behavior

For a breached email address, the connector enriches in place — no extra entities are created, keeping the graph clean:

- the observable's **score** is set from the XposedOrNot risk score (0–100, free API);
- **labels** `data-breach` — and `plaintext-password-exposure` when at least one breach stored passwords in plaintext — are added to the observable;
- an **external reference** to xposedornot.com is attached;
- a markdown **Note** is attached with the full breach table: breach name, date, records exposed, affected domain, exposed data classes, password-storage risk and verification status, plus first/latest exposure years and totals.

A clean email (not found in any breach) completes with an explicit "no known breach exposure" message and modifies nothing. Rate limiting (HTTP 429) is retried with backoff honoring `Retry-After`; persistent rate limiting fails that single enrichment with a log message recommending the optional key — the connector itself keeps running.

## Legal and privacy notice

Only the observable's email address is sent, over TLS, to xposedornot.com — nothing else leaves the platform. Breach exposure tied to an email address is **personal information**: gate what may be enriched with `XPOSEDORNOT_MAX_TLP`, apply a restrictive `XPOSEDORNOT_TLP_LEVEL` to results, and use them only within lawful, authorised investigations (GDPR / legal basis / proper investigative framework). See the [XposedOrNot privacy policy](https://xposedornot.com/privacy).

## Debugging

Set `CONNECTOR_LOG_LEVEL=debug`. All API errors are logged through the connector logger with masked context; the API key never appears in logs. Typical messages:

- `XposedOrNot rate limited (keyless: 2/s, 25/hour); backing off.` — expected under keyless bursts; configure a key for volume.
- `XposedOrNot: API key rejected or missing for the Plus API` — check `XPOSEDORNOT_API_KEY`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Connector Configurations

Below is an exhaustive enumeration of all configurable parameters available, each accompanied by detailed explanations of their purposes, default behaviors, and usage guidelines to help you understand and utilize them effectively.

### Type: `object`

| Property | Type | Required | Possible values | Default | Description |
| -------- | ---- | -------- | --------------- | ------- | ----------- |
| OPENCTI_URL | `string` | ✅ | Format: [`uri`](https://json-schema.org/understanding-json-schema/reference/string#built-in-formats) | | The OpenCTI platform URL. |
| OPENCTI_TOKEN | `string` | ✅ | string | | The token of the user who represents the connector in the OpenCTI platform. |
| CONNECTOR_NAME | `string` | | string | `"XposedOrNot"` | Name of the connector. |
| CONNECTOR_SCOPE | `array` | | string | `["Email-Addr"]` | The scope or type of data the connector is importing, either a MIME type or Stix Object (for information only). |
| CONNECTOR_TYPE | `string` | | string | `"INTERNAL_ENRICHMENT"` | Should always be set to INTERNAL_ENRICHMENT for this connector. |
| CONNECTOR_LOG_LEVEL | `string` | | `debug` `info` `warn` `warning` `error` | `"error"` | Determines the verbosity of the logs. |
| CONNECTOR_AUTO | `boolean` | | boolean | `false` | Enables or disables automatic enrichment of observables. The keyless community API is rate limited (2/s, 25/hour per IP); keep disabled or configure an API key before enabling on busy platforms. |
| XPOSEDORNOT_API_KEY | `string` | | Format: [`password`](https://json-schema.org/understanding-json-schema/reference/string#built-in-formats) | | Optional XposedOrNot API key (console.xposedornot.com). When set, the connector uses the commercial Plus API with higher rate limits. The connector is fully functional without it. |
| XPOSEDORNOT_API_BASE_URL | `string` | | Format: [`uri`](https://json-schema.org/understanding-json-schema/reference/string#built-in-formats) | `"https://api.xposedornot.com"` | Base URL of the free XposedOrNot community API. |
| XPOSEDORNOT_MAX_TLP | `string` | | `TLP:CLEAR` `TLP:WHITE` `TLP:GREEN` `TLP:AMBER` `TLP:AMBER+STRICT` `TLP:RED` | `"TLP:AMBER"` | Maximum TLP of an observable the connector is allowed to enrich. The observable's email address is sent to the XposedOrNot API. |
| XPOSEDORNOT_TLP_LEVEL | `string` | | `clear` `green` `amber` `amber+strict` `red` | `"amber"` | Traffic Light Protocol (TLP) level applied to the objects imported into OpenCTI. Results contain personal data; a restrictive TLP is recommended. |
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://www.filigran.io/connectors/xposedornot_config.schema.json",
"type": "object",
"properties": {
"OPENCTI_URL": {
"description": "The OpenCTI platform URL.",
"format": "uri",
"maxLength": 2083,
"minLength": 1,
"type": "string"
},
"OPENCTI_TOKEN": {
"description": "The token of the user who represents the connector in the OpenCTI platform.",
"type": "string"
},
"CONNECTOR_NAME": {
"default": "XposedOrNot",
"description": "Name of the connector.",
"type": "string"
},
"CONNECTOR_SCOPE": {
"default": [
"Email-Addr"
],
"description": "The scope or type of data the connector is importing, either a MIME type or Stix Object (for information only).",
"items": {
"type": "string"
},
"type": "array"
},
"CONNECTOR_TYPE": {
"default": "INTERNAL_ENRICHMENT",
"description": "Should always be set to INTERNAL_ENRICHMENT for this connector.",
"type": "string"
},
"CONNECTOR_LOG_LEVEL": {
"default": "error",
"description": "Determines the verbosity of the logs.",
"enum": [
"debug",
"info",
"warn",
"warning",
"error"
],
"type": "string"
},
"CONNECTOR_AUTO": {
"default": false,
"description": "Enables or disables automatic enrichment of observables. The keyless community API is rate limited (2/s, 25/hour per IP); keep disabled or configure an API key before enabling on busy platforms.",
"type": "boolean"
},
"XPOSEDORNOT_API_KEY": {
"description": "Optional XposedOrNot API key (console.xposedornot.com). When set, the connector uses the commercial Plus API with higher rate limits. The connector is fully functional without it.",
"format": "password",
"type": "string"
},
"XPOSEDORNOT_API_BASE_URL": {
"default": "https://api.xposedornot.com",
"description": "Base URL of the free XposedOrNot community API.",
"format": "uri",
"type": "string"
},
"XPOSEDORNOT_MAX_TLP": {
"default": "TLP:AMBER",
"description": "Maximum TLP of an observable the connector is allowed to enrich. The observable's email address is sent to the XposedOrNot API.",
"enum": [
"TLP:CLEAR",
"TLP:WHITE",
"TLP:GREEN",
"TLP:AMBER",
"TLP:AMBER+STRICT",
"TLP:RED"
],
"type": "string"
},
"XPOSEDORNOT_TLP_LEVEL": {
"default": "amber",
"description": "Traffic Light Protocol (TLP) level applied to the objects imported into OpenCTI. Results contain personal data; a restrictive TLP is recommended.",
"enum": [
"clear",
"green",
"amber",
"amber+strict",
"red"
],
"type": "string"
}
},
"required": [
"OPENCTI_URL",
"OPENCTI_TOKEN"
],
"additionalProperties": true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"title": "XposedOrNot",
"slug": "xposedornot",
"description": "XposedOrNot is an open, free data-breach search service tracking 760+ breaches. Given an email address, it returns the breaches the address appears in, with per-breach detail: breach date, records exposed, exposed data classes, affected domain, industry and password-storage risk, plus an overall risk score.\n\nIntegrating XposedOrNot with OpenCTI lets analysts enrich an Email-Addr observable in place: the observable receives a risk-derived score, `data-breach` labels (plus a `plaintext-password-exposure` label when applicable) and an external reference, and a markdown summary note with the full breach table is attached. The connector works without any API key via the free community API (rate limits apply); an optional commercial key raises limits. Breach exposure tied to an email address is personal information and must only be used for lawful, authorised investigations (GDPR / legal basis / proper investigative framework).",
"short_description": "Enrich an email address with its data-breach exposure — breach names, dates, records, exposed data classes and password-storage risk — score and label the observable, and attach a summary note. No API key required.",
"logo": "internal-enrichment/xposedornot/__metadata__/logo.png",
"use_cases": [
"Enrichment & Analysis"
],
"verified": false,
"last_verified_date": null,
"playbook_supported": true,
"max_confidence_level": 75,
"support_version": ">= 6.8.12",
"subscription_link": "https://console.xposedornot.com",
"source_code": "https://github.com/OpenCTI-Platform/connectors/tree/master/internal-enrichment/xposedornot",
"manager_supported": false,
"container_version": "rolling",
"container_image": "opencti/connector-xposedornot",
"container_type": "INTERNAL_ENRICHMENT"
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions internal-enrichment/xposedornot/config.yml.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
opencti:
url: 'http://localhost:8080'
token: 'ChangeMe'

connector:
type: 'INTERNAL_ENRICHMENT'
id: 'ChangeMe'
name: 'XposedOrNot' # optional (default: 'XposedOrNot')
scope: 'Email-Addr' # optional (default: 'Email-Addr')
log_level: 'info' # optional (default: 'error')
auto: false # optional, auto-enrich observables. Keyless API is rate limited; keep false or set an api_key (default: false)

xposedornot:
api_key: 'ChangeMe' # optional; remove/leave empty to use the free keyless community API
api_base_url: 'https://api.xposedornot.com' # optional (default: community API)
max_tlp: 'TLP:AMBER' # optional, max TLP of observables the connector may enrich (default: 'TLP:AMBER')
tlp_level: 'amber' # optional, TLP applied to produced objects: clear, green, amber, amber+strict, red (default: 'amber')
17 changes: 17 additions & 0 deletions internal-enrichment/xposedornot/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
version: '3'
services:
connector-xposedornot:
image: opencti/connector-xposedornot:latest
environment:
- OPENCTI_URL=http://localhost
- OPENCTI_TOKEN=ChangeMe
- CONNECTOR_ID=ChangeMe
- CONNECTOR_NAME=XposedOrNot
- CONNECTOR_SCOPE=Email-Addr
- CONNECTOR_AUTO=false # keyless API is rate limited (25/hour); enable with care or configure an API key
- CONNECTOR_LOG_LEVEL=error
- XPOSEDORNOT_API_KEY= # optional; empty uses the free keyless community API
- XPOSEDORNOT_API_BASE_URL=https://api.xposedornot.com
- XPOSEDORNOT_MAX_TLP=TLP:AMBER # maximum TLP of observables the connector may enrich
- XPOSEDORNOT_TLP_LEVEL=amber # clear, green, amber, amber+strict, red
restart: always
3 changes: 3 additions & 0 deletions internal-enrichment/xposedornot/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]
1 change: 1 addition & 0 deletions internal-enrichment/xposedornot/src/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# -*- coding: utf-8 -*-
21 changes: 21 additions & 0 deletions internal-enrichment/xposedornot/src/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
import sys
import time
import traceback

from pycti import OpenCTIConnectorHelper

from .xposedornot import ConnectorSettings, XposedOrNotConnector

if __name__ == "__main__":
try:
settings = ConnectorSettings()
helper = OpenCTIConnectorHelper(
config=settings.to_helper_config(),
playbook_compatible=True,
)
XposedOrNotConnector(config=settings, helper=helper).run()
except Exception:
traceback.print_exc()
time.sleep(10)
sys.exit(1)
4 changes: 4 additions & 0 deletions internal-enrichment/xposedornot/src/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pycti==7.260710.0
pydantic~=2.11.3
requests>=2.32,<3.0.0
connectors-sdk @ git+https://github.com/OpenCTI-Platform/connectors.git@master#subdirectory=connectors-sdk
5 changes: 5 additions & 0 deletions internal-enrichment/xposedornot/src/xposedornot/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
from .connector import XposedOrNotConnector
from .settings import ConnectorSettings

__all__ = ["XposedOrNotConnector", "ConnectorSettings"]
Loading
Loading