Skip to content

feat(ipgeolocation): add IPGeolocation.io internal enrichment connector#7013

Open
mateen993 wants to merge 2 commits into
OpenCTI-Platform:masterfrom
mateen993:feat/connector-ipgeolocation-io
Open

feat(ipgeolocation): add IPGeolocation.io internal enrichment connector#7013
mateen993 wants to merge 2 commits into
OpenCTI-Platform:masterfrom
mateen993:feat/connector-ipgeolocation-io

Conversation

@mateen993

Copy link
Copy Markdown

Proposed changes

  • Add a new internal enrichment connector for [IPGeolocation.io](https://ipgeolocation.io) that enriches IPv4 and IPv6 observables using the IPGeolocation.io v3 APIs (IP Location, IP Security, ASN, and Abuse Contact).
  • The connector transforms raw API responses into semantically rich STIX 2.1 knowledge — Location (country + city with coordinates), Autonomous System, Organization/ISP/Cloud provider identities, abuse contact identities, domain-name hostnames, STIX indicators for high-risk IPs, analyst-readable markdown notes with security narratives and risk explanations, and meaningful relationships (located-at, belongs-to, related-to, resolves-to).
  • Includes a weighted risk scoring algorithm that normalizes threat signals (VPN, proxy, TOR, bot, spam, known-attacker, residential proxy, cloud provider) into a unified 0–100 score with human-readable explanations instead of raw booleans.
  • Supports two credit-optimization modes: a single-call mode using /v3/ipgeo?include=security,abuse to minimize HTTP requests, and a dedicated-endpoint mode calling /v3/security, /v3/asn, /v3/abuse separately for granular control.
  • Every enrichment feature (labels, indicators, relationships, notes, opinions, markdown summary) is independently toggleable via environment variables.

Checklist

  • I consider the submitted work as finished
  • I have signed my commits using GPG key.
  • I tested the code for its functionality using different use cases
  • I added/update the relevant documentation (either on github or on notion)
  • Where necessary I refactored code to improve the overall quality

Further comments

APIs consumed (4 endpoints, configurable independently):

Endpoint Data Credits
/v3/ipgeo Geolocation, ASN, company, timezone, network, currency 1
/v3/security Threat score, VPN/proxy/TOR/bot/spam/attacker flags, provider names, confidence scores 2
/v3/asn Routes, peers, upstreams, downstreams, WHOIS, RIR, allocation 1
/v3/abuse Abuse email, phone, organization, address, route 1

STIX objects created: Location (Country), Location (City), Autonomous-System, Identity (Organization), Identity (Cloud Provider), Identity (Abuse Contact), Domain-Name (hostname), Indicator (for high-risk IPs), Note (markdown), Opinion (optional), Relationship, Labels.

Copilot AI review requested due to automatic review settings July 15, 2026 10:43
@filigran-cla-bot filigran-cla-bot Bot added the cla:pending CLA signature required. label Jul 15, 2026
@filigran-cla-bot

filigran-cla-bot Bot commented Jul 15, 2026

Copy link
Copy Markdown

Contributor License Agreement

CLA signed 💚

Thank you @mateen993 for signing the Contributor License Agreement! Your pull request can now be reviewed and merged.

We appreciate your contribution to Filigran's open source projects! ❤️

This is an automated message from the Filigran CLA Bot.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new internal-enrichment connector under internal-enrichment/ipgeolocation that calls IPGeolocation.io v3 endpoints, computes a unified risk score, and emits STIX 2.1 objects (locations, ASN, identities, indicators, notes, relationships) for IPv4/IPv6 observables in OpenCTI.

Changes:

  • Introduces the connector runtime (config, API client, enrichment orchestration) plus Docker packaging.
  • Implements typed response models, risk scoring, markdown note generation, and STIX mapping/bundling.
  • Adds unit tests and connector documentation (architecture, STIX mapping, risk scoring, testing guide).

Reviewed changes

Copilot reviewed 25 out of 26 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
internal-enrichment/ipgeolocation/src/api_client.py HTTP client for IPGeolocation.io v3 endpoints with retries + enrichment modes
internal-enrichment/ipgeolocation/src/config.py Typed env/YAML configuration for OpenCTI + connector + IPGeolocation settings
internal-enrichment/ipgeolocation/src/connector.py OpenCTI enrichment worker: read observable → call APIs → score → map to STIX → send bundle
internal-enrichment/ipgeolocation/src/main.py Entrypoint wiring connector startup/error handling
internal-enrichment/ipgeolocation/src/markdown_generator.py Generates analyst-readable markdown notes from intel + risk assessment
internal-enrichment/ipgeolocation/src/models.py Dataclass models + merge helpers for the various API response blocks
internal-enrichment/ipgeolocation/src/risk_scorer.py Unified risk scoring + confidence derivation + explanation builder
internal-enrichment/ipgeolocation/src/stix_mapper.py STIX 2.1 object factory + relationship/label/indicator/note generation
internal-enrichment/ipgeolocation/src/init.py Package metadata/version
internal-enrichment/ipgeolocation/tests/conftest.py Shared pytest fixtures for mapper/scorer/markdown/intel samples
internal-enrichment/ipgeolocation/tests/mock_responses.py Mocked API payloads used across unit tests
internal-enrichment/ipgeolocation/tests/test_api_client.py Unit tests for API client enrichment modes and error handling
internal-enrichment/ipgeolocation/tests/test_markdown_generator.py Unit tests for markdown note sections/content
internal-enrichment/ipgeolocation/tests/test_risk_scorer.py Unit tests for scoring thresholds, caps, confidence, and opinion mapping
internal-enrichment/ipgeolocation/tests/test_stix_mapper.py Unit tests validating key STIX objects/relationships/labels/indicators/notes
internal-enrichment/ipgeolocation/tests/init.py Marks tests as a package
internal-enrichment/ipgeolocation/requirements.txt Python dependencies for the connector
internal-enrichment/ipgeolocation/README.md Connector user docs: features, configuration reference, testing notes
internal-enrichment/ipgeolocation/docs/ARCHITECTURE.md Component/data-flow documentation
internal-enrichment/ipgeolocation/docs/RISK_SCORING.md Detailed scoring algorithm reference
internal-enrichment/ipgeolocation/docs/STIX_MAPPING.md Mapping spec for emitted STIX objects/relationships/labels
internal-enrichment/ipgeolocation/docs/TESTING_GUIDE.md End-to-end deployment/testing walkthrough
internal-enrichment/ipgeolocation/Dockerfile Container build for running connector
internal-enrichment/ipgeolocation/entrypoint.sh Container entrypoint script
internal-enrichment/ipgeolocation/docker-compose.yml Example compose service definition for running the connector
internal-enrichment/ipgeolocation/config.yml.sample Sample YAML configuration for local runs

Comment thread internal-enrichment/ipgeolocation/src/config.py
Comment on lines +72 to +82
self._author = stix2.Identity(
id=_det_id("identity", author_name),
name=author_name,
identity_class="organization",
description=(
"IPGeolocation.io — Enterprise-grade IP geolocation "
"and threat intelligence provider."
),
created_by_ref=None,
allow_custom=True,
)
Comment on lines +386 to +392
return stix2.DomainName(
value=intel.hostname,
allow_custom=True,
custom_properties={
"created_by_ref": self._author.id,
},
)
Comment on lines +522 to +534
custom = {
"x_opencti_score": risk.opencti_score,
"x_opencti_description": risk.explanation,
}
kwargs: dict[str, Any] = {
"id": observable_id,
"value": ip_val,
"allow_custom": True,
"custom_properties": custom,
}
if ext_refs:
kwargs["external_references"] = ext_refs

| IP Observable | `located-at` | Country | Always (if country exists) |
| City | `located-at` | Country | Always (if city exists) |
| IP Observable | `belongs-to` | ASN | Always (if ASN exists) |
| ASN | `belongs-to` | Organization | Always (if org exists) |
@@ -0,0 +1,5 @@
#!/bin/bash
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@mateen993

Copy link
Copy Markdown
Author

Hi @ncarenton, please let us know when you are available to review this merge/pull request?

@romain-filigran romain-filigran added the community Contribution from the community. label Jul 24, 2026
@romain-filigran
romain-filigran requested a review from Copilot July 24, 2026 21:40
@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

⚠️ JUnit XML file not found

The CLI was unable to find any JUnit XML files to upload.
For more help, visit our troubleshooting guide.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 26 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (2)

internal-enrichment/ipgeolocation/src/stix_mapper.py:390

  • For SCOs like domain-name, OpenCTI expects the author to be set via the extension property x_opencti_created_by_ref (not created_by_ref). Using created_by_ref inside custom_properties will not set the author correctly and can also break strict STIX validation when the platform re-emits the bundle.
        return stix2.DomainName(
            value=intel.hostname,
            allow_custom=True,
            custom_properties={
                "created_by_ref": self._author.id,

internal-enrichment/ipgeolocation/src/stix_mapper.py:536

  • external_references is being attached to an IPv4/IPv6 SCO as a top-level property. For OpenCTI observable upserts, external refs should be provided via custom_properties['x_opencti_external_references'] (and similarly labels via x_opencti_labels, author via x_opencti_created_by_ref). As written, this is likely to raise a validation/constructor error or be ignored by the platform.
        if ext_refs:
            kwargs["external_references"] = ext_refs

        if "IPv6" in observable_type:
            return stix2.IPv6Address(**kwargs)

Comment on lines +45 to +59
# ---------------------------------------------------------------------------
# TLP marking definitions (re-usable singletons)
# ---------------------------------------------------------------------------
TLP_WHITE = stix2.TLP_WHITE
TLP_GREEN = stix2.TLP_GREEN
TLP_AMBER = stix2.TLP_AMBER
TLP_RED = stix2.TLP_RED

TLP_MAP = {
"TLP:WHITE": TLP_WHITE,
"TLP:CLEAR": TLP_WHITE,
"TLP:GREEN": TLP_GREEN,
"TLP:AMBER": TLP_AMBER,
"TLP:RED": TLP_RED,
}
Comment on lines +12 to +14
RUN apt-get update && \
apt-get install -y --no-install-recommends libmagic1 && \
rm -rf /var/lib/apt/lists/*
Comment on lines +177 to +182
## Testing

```bash
pip install pytest
pytest tests/ -v
```
Comment on lines +65 to +70
## Quick Start

### Docker Compose (recommended)

1. Copy `docker-compose.yml` into your OpenCTI deployment.
2. Set environment variables (or use `.env`):
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community Contribution from the community.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants