Skip to content

Karzen/ThreatIntelligenceAPI

Repository files navigation

Man Detective Threat Intelligence API Man Detective

.NET C# ASP.NET Core EF Core PostgreSQL Docker xUnit

Tests

A cybersecurity-themed REST API built in C# ASP.NET Core that accepts IP addresses, assesses them against threat sources, persists results, and reacts to findings via domain events. Built as a university project to demonstrate Clean Architecture, SOLID principles, and design patterns in a real-world context.


What it does

  • Submit an IP address and get back a threat assessment (malicious/clean, severity, type)
  • Results are cached — the same IP scanned within 6 hours returns the existing result without burning API quota
  • Full scan history with filtering by time range and IP
  • Aggregate statistics: total scans, breakdowns by type and severity, top threats
  • Domain events fire when a malicious IP is found, notifying independent subscribers (audit log, metrics)
  • Scanner fallback chain — VirusTotal when an API key is configured, local blocklist otherwise

Quick start

Prerequisites: .NET 10 SDK, Docker Desktop

# Clone
git clone https://github.com/Karzen/ThreatIntelligenceAPI.git
cd ThreatIntelligenceAPI

# Start Postgres
docker compose up -d

# Run the API
cd ThreatIntelAPI
dotnet run

Open the API explorer at http://localhost:5042/scalar/


API endpoints

Method Endpoint Description
POST /api/threats/scan Submit an IP for threat assessment
GET /api/threats/history/{ip} All scans for a specific IP
GET /api/threats/malicious All malicious IPs found
GET /api/threats/stats Aggregate statistics
DELETE /api/threats/history/old Delete entries older than N days

Example — scan a known malicious IP

Request:

POST /api/threats/scan
Content-Type: application/json

{
  "ipAddress": "185.220.101.45"
}

Response:

{
  "ipAddress": "185.220.101.45",
  "threatType": "Malware",
  "isMalicious": true,
  "detectionCount": 1,
  "detectedAt": "2026-03-21T10:49:26.3301923Z",
  "summary": "[Malicious] 185.220.101.45 | Type: Malware | TotalDetections: 1 | DetectedAt: 3/21/2026 10:49:26 AM"
}

Example — statistics

GET /api/threats/stats
{
  "totalScans": 15,
  "totalMalicious": 7,
  "byType": {
    "Malware": 3,
    "Blocklisted": 4
  },
  "topThreats": [
    "89.248.172.16",
    "185.220.101.45",
    "185.220.101.45",
    "89.248.172.16",
    "194.165.16.11"
  ]
}

Configuration

appsettings.json — production (VirusTotal):

{
  "ConnectionStrings": {
    "Default": "Host=localhost;Database=threatdb;Username=postgres;Password=postgres"
  },
  "ThreatScanner": {
    "Type": "VirusTotal",
    "ApiKey": "your-virustotal-api-key",
    "TimeoutMs": 5000,
    "EnableFallback": true,
    "BlocklistIPs": []
  }
}

appsettings.Development.json — local dev (no API key needed):

{
  "ThreatScanner": {
    "Type": "LocalBlocklist",
    "EnableFallback": false
  }
}

The API auto-migrates the database on startup — no manual dotnet ef database update needed.


Architecture

This project follows Clean Architecture — dependencies point inward only. The domain layer has zero knowledge of databases, HTTP clients, or frameworks.

Architecture light Architecture dark


Design patterns

Strategy

ThreatService holds an ordered list of IThreatScanner implementations. At scan time it picks the first available one. VirusTotal is tried first; the local blocklist is the fallback. Adding a new scanner provider requires zero changes to ThreatService.

Decorator

Caching and logging are added to the scanner stack without modifying VirusTotalScanner. The decorator chain is: CachingScanner -> LoggingScanner -> VirusTotalScanner. ThreatService holds the outermost layer and has no knowledge of the stack.

Repository

ThreatService calls methods like WasRecentlyScannedAsync and GetMaliciousAsync. It has no idea whether data comes from Postgres, SQLite, or a mock. Swapping the database means writing one new class and one registration line.

Factory

ThreatScannerFactory uses a dictionary of factory functions keyed by scanner type name. Adding a new scanner type is one entry in the dictionary.

_builders = new Dictionary<string, Func<IThreatScanner>>(StringComparer.OrdinalIgnoreCase)
{
    ["VirusTotal"]    = CreateVirusTotal,
    ["LocalBlocklist"] = CreateLocalBlocklist,
};

Observer — domain events

When a malicious IP is detected, ThreatService fires a ThreatDetected event. Subscribers (audit logger, metrics tracker) react independently. Adding a new reaction — Slack notification, webhook — means writing one new subscriber class. ThreatService never changes.


Running the tests

cd ThreatIntelAPI.Tests
dotnet test --verbosity normal

27 tests covering three layers:

ThreatEntryTests      -> domain model validation, methods
ThreatServiceTests    -> scan flow, cache hits, observer events, scanner fallback
RepositoryTests       -> all 8 repository methods against EF Core in-memory DB

Tests use Moq for mocking and FluentAssertions for readable assertions. The repository tests use EF Core's in-memory provider.

About

IP threat assessment API built with ASP.NET Core.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages