diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index e82df2f..a4e9617 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -66,6 +66,21 @@ jobs: - name: Install npm dependencies run: npm ci + - name: Generate Version + id: version + run: | + # Generate build timestamp in the same format as the app (yyyyMMdd-HHmm) + BUILD_TIMESTAMP=$(date -u +"%Y%m%d-%H%M") + BUILD_ID=$(echo "${{ github.sha }}" | cut -c1-8) + VERSION="${BUILD_TIMESTAMP}-${BUILD_ID}" + + echo "version=${VERSION}" >> $GITHUB_OUTPUT + echo "build-timestamp=${BUILD_TIMESTAMP}" >> $GITHUB_OUTPUT + echo "build-id=${BUILD_ID}" >> $GITHUB_OUTPUT + echo "Preview version: ${VERSION}" + echo "Build timestamp: ${BUILD_TIMESTAMP}" + echo "Build ID: ${BUILD_ID}" + - name: Generate Preview Name id: preview run: | @@ -95,9 +110,11 @@ jobs: CLOUDFLARE_RADAR_API_TOKEN: ${{ secrets.CLOUDFLARE_RADAR_API_TOKEN_DEV }} SkipCodeGeneration: false ALBATROSS_ENVIRONMENT: preview + BUILD_TIMESTAMP: ${{ steps.version.outputs.build-timestamp }} + BUILD_ID: ${{ steps.version.outputs.build-id }} run: | - # Generate authentication key with environment context - ./Generate-AuthKey.ps1 -OutputPath "Generated" -Environment "preview" -Verbose + # Generate authentication key with environment context and build info + ./Generate-AuthKey.ps1 -OutputPath "Generated" -Environment "preview" -BuildTimestamp "$env:BUILD_TIMESTAMP" -BuildId "$env:BUILD_ID" -Verbose # Build application (this generates both SPA and Worker files) dotnet build --configuration Release --no-restore diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index 3d89970..7169636 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -92,9 +92,11 @@ jobs: CLOUDFLARE_RADAR_API_TOKEN: ${{ secrets.CLOUDFLARE_RADAR_API_TOKEN }} SkipCodeGeneration: false ALBATROSS_ENVIRONMENT: production + BUILD_TIMESTAMP: ${{ steps.version.outputs.build-timestamp }} + BUILD_ID: ${{ steps.version.outputs.build-id }} run: | - # Generate authentication key with environment context - ./Generate-AuthKey.ps1 -OutputPath "Generated" -Environment "production" -Verbose + # Generate authentication key with environment context and build info + ./Generate-AuthKey.ps1 -OutputPath "Generated" -Environment "production" -BuildTimestamp "$env:BUILD_TIMESTAMP" -BuildId "$env:BUILD_ID" -Verbose # Build application (this generates both SPA and Worker files) dotnet build --configuration Release --no-restore diff --git a/Generate-AuthKey.ps1 b/Generate-AuthKey.ps1 index 53af0e7..02790d4 100644 --- a/Generate-AuthKey.ps1 +++ b/Generate-AuthKey.ps1 @@ -6,6 +6,8 @@ param( [string]$OutputPath = ".", [string]$KeyLength = "32", [string]$Environment = "production", + [string]$BuildTimestamp = "", + [string]$BuildId = "", [switch]$Verbose ) @@ -64,9 +66,22 @@ try { Write-BuildLog "Created output directory: $OutputPath" } - # Generate build timestamp - $buildTimestamp = Get-Date -Format "yyyyMMdd-HHmm" - $buildGuid = [System.Guid]::NewGuid().ToString("N").Substring(0, 8) + # Generate build timestamp and ID (use provided values or generate new ones) + if ([string]::IsNullOrWhiteSpace($BuildTimestamp)) { + $buildTimestamp = Get-Date -Format "yyyyMMdd-HHmm" + Write-BuildLog "Generated build timestamp: $buildTimestamp" + } else { + $buildTimestamp = $BuildTimestamp + Write-BuildLog "Using provided build timestamp: $buildTimestamp" + } + + if ([string]::IsNullOrWhiteSpace($BuildId)) { + $buildGuid = [System.Guid]::NewGuid().ToString("N").Substring(0, 8) + Write-BuildLog "Generated build ID: $buildGuid" + } else { + $buildGuid = $BuildId + Write-BuildLog "Using provided build ID: $buildGuid" + } # Create the generated constants file for C# $csharpContent = @" diff --git a/Pages/Home.razor b/Pages/Home.razor index f8a40f1..486ac44 100644 --- a/Pages/Home.razor +++ b/Pages/Home.razor @@ -6,7 +6,7 @@ @using System.Linq @inject IJSRuntime JSRuntime -Albatross +Albatross - Cloud IP Analysis & Reputation Checker @@ -107,9 +107,20 @@ \____/_/\____/\__,_/\__,_/ /___/_/ /____/\___/\__,_/_/ \___/_/ /_/
--> -CloudIPSearch
-ascii-text-art-albatross

-Albatross

+CloudIPSearch
+ascii-text-art-albatross

+ +

@@ -119,6 +130,9 @@ +

@if (!string.IsNullOrEmpty(statusMessage)) @@ -142,6 +156,7 @@
  • Cloud Detection: Identifies if IPs belong to AWS, Azure, GCP, or Oracle Cloud
  • Abuse Checking: Queries AbuseIPDB for security reputation and threat intelligence
  • Network Information: Provides ASN details and network ownership via Cloudflare Radar
  • +
  • AI-Powered Analysis: Uses Cloudflare Workers AI (Llama 3.1 70B) to generate risk assessments and actionable recommendations
  • 📝 Input Formats:
    @@ -151,10 +166,13 @@
  • 2001:4860:4860::8888 - IPv6 addresses supported
  • +
    🤖 AI Reputation Analysis:
    +

    Automatically analyzes IP reputation data using Cloudflare Workers AI with Llama 3.1 70B Instruct model. Provides risk level assessment (low/medium/high/critical), trust score (0-100), summary of abuse event patterns, and security recommendations based on real-time threat intelligence.

    +
    🔒 Security:

    All requests use HMAC-SHA256 authentication with timestamp validation. Only public routable IP addresses are accepted (private networks like 10.x.x.x, 192.168.x.x, and 127.x.x.x are blocked).

    -

    Data provided by AbuseIPDB, Cloudflare Radar, and official cloud provider IP manifests.

    +

    Data provided by AbuseIPDB, Cloudflare Radar, Cloudflare Workers AI, and official cloud provider IP manifests.

    @@ -281,7 +299,12 @@ -@code { private string ipAddress = string.Empty;private string statusMessage = string.Empty; +@code { + private const int AsciiArtPixelMatrixColumns = 240; + private const int AsciiArtPixelMatrixRows = 44; + private static readonly int[][] AsciiArtPixelMatrix = BuildAsciiArtPixelMatrix(); + + private string ipAddress = string.Empty;private string statusMessage = string.Empty; private List? azureMatchedServices; private List? awsMatchedServices; private List? gcpMatchedServices; @@ -290,8 +313,72 @@ private bool showDescription = false; private bool isCheckingAbuse = false; private bool showJson = true; // Default to showing JSON + private bool enableAI = true; // Default to AI enabled private string formattedJson = string.Empty; private Albatross.Services.AbuseIPDBApiResponse? abuseIpResult; + + private static string GetPixelShadeClass(int value) => value switch + { + 1 => "pixel-matrix__pixel--edge", + 2 => "pixel-matrix__pixel--mid", + 3 => "pixel-matrix__pixel--core", + 4 => "pixel-matrix__pixel--core", + _ => "pixel-matrix__pixel--empty" + }; + + private static int[][] BuildAsciiArtPixelMatrix() + { + string rawMatrix = @"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111222222222222222221000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022223444444444444444442100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000122223433333333333333332100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001222223333344444333334343444433332100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001222223433444444444434333334334342100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111111111222333334433344443333334333434434343222200000000000000000000000000000000000000000000000111111222222222222222222222111100000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000022222222223444444444434334342121244343434434334444310000000000000000000000000000000000000000000000122222344444444444444444444222100000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000022222222223433444444433334343222233333334333333334311000000000000000000000000000000000000000000111222222343433333333333444434222110000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000122222222222233444444443444334444334434343444343444434343434334321000000000000000000000000000122223433443444444444444444444433433343433344433334211000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000122222222222244444444443333333333334334333334333334334333433334311000000000000000000000000000122223333333444444444444444444444433444444443443334211000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000111111111222333333333332333444443434333434334334333434333434434333434334333221100000000000000001111111233333444444444444444444444444322334322323234334333333221100000000000000 +000000000000000000000000000000000000000000000000000000000000000001222222222223444444444421213443443434333434334334333434333334434333434333444442110000000000000002222222344443444444443333333333333334221344211212224224344444432100000000000000 +000000000000000000000000000000000000000000000000000000000011111111222222222223333443333322223333333333333333334334333334333334333333433333333332211100001111110012222222243444444444443344433333433334322334322222223223333333332211000000000000 +000000000000000000000000000000000000000000000000000011222234444444444444444443222221111122222222233444334444334434343434343444434343434343444334444322223444442222222222243444444443333344433343434343444433444444432222222222344444211000000000 +000000000000000000000000000000000000000000000000000001222234333334444444444443222221111122222222244433333333334333333333333334333333333333334333334322223333332222222222343444444444444444434333333333433333333333432222222222344444211000000000 +000000000000000000000000000000111111111111112333333333443444444343222222222221000000000000112211222233333434334334343434343444434333434333444333434333333344333333332222223444444322222222332343433333443433343433433333333333222222333210000000 +000000000000000000000000000000111111111111222444444444444444444443222221111110000000000000011211222244433433334334333334333334434333434333444333333444444344334444342222123344434322222222341344444444433433343433344444444444222222344210000000 +000000000000000000000001111111111111111111222333333333333333333332111111111110000000000000001111222233333333334334333444333334434333434333434333333333333344333334443333234333333211111221232333333333343333343333343333333334322222333322100000 +000000000000000000000012222222222111122222211111111111111111111110000000000000000000000000000111222222234434334334343444343444434343434343444333444333444344343444444444444322101000000011112222211112343433343433344433343433444444434444211000 +000000000000000000000012222222222111122222211111111111111111111110000000000000000000000000000111222222233433334334333444333334333333333333334333333333333344333334444444434322111000000011112222221112343433343333333433333333333333433333211000 +000000000000000111111111111111111111111111111233333333322333333321110000000000000000000000000001111112222223434444444444343444434343434343444333444333444344343434444444444433322111000000000001111112222124332333333432343434344434434343333211 +000000000000000111111111111110111111111011111234444444422444443421110000000000000000000000000000111112222223434444444334333334434333434333434333333333333344333433333344444434312101000000000001111112222114431344444431343333333333433333434210 +000000000000112111111111111122222222222222111122222222222233333422111211000000000000000000000000100001111212334444444334333334434333434333434333334333334344333434343434444434323222211100000000000011111112221222222332343344444344444444444322 +000000000111234222222222222234444444444443211111111111111112443422223421100000000000000000000000000000000111234333333434343444434333434333444333434333434344343434343433333343444444442100000000000000000001111111110344444444444444444444444444 +000000000111233222222222222233333333333343211111111111111112333322222422100000000000000000000000000000000111233334333334333334333333433333334333333333333344333433333434344333333333332110000000000000000001111111111333333344444344444444444333 +000111221111111111111122333333334333344443333321000000000000111111233333333110000000000000000000000000000000012244343434343444434343434343444333444333444344343434343434344344344443433333332000000000000000000000000111111111111111111111111111 +000111222111111111111112334444344444443333444421000000000000111111234444443210000000000000000000000000000000011233333334333334334333434333334333333333333344333434333434344333344443333444432000000000000000000000000111111111111111111111111111 +011111111100000011111122333333433333334343322221000000000000000001122222222111000000000000000000000000001112223344333434333434434333434333444333334333334344333434343434344343334443333343433210000000000000000000000000000000000000000000000000 +112111111100000001111122443222422222234443211110000000000000000000111111111121110000000000000000000000111223444434343434343434434333434333444333334333334344343434343434344343433333333344434310000000000000000000000000000000000000000000000000 +112111111100000001111112333222322222233333211110000000000000000000111111111121110000000000000000000001111223434334333334333334333333433333334333333333333344333433333433344333333333333343334320000000000000000000000000000000000000000000000000 +111000000000000000000000111111112222422111100000000000000000000000000111111111111111222222222222222222222223434434323434343444434343434343444333444333444344343434343434344344433444433444434222210000000000000000000000000000000000000000000000 +111000000000000000000000111111112222432111100000000000000000000000000111111111111111222222222222222222222223444334313444333334334333434333334333333333333344333433333433344333333333333343334222210000000000000000000000000000000000000000000000 +000000000000000000000000000000001111111000000000000000000000000000001222222222222222222222222222222222222222223434333323433434433333433333434333434333434344343434343434344333333343333343434333331100000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000001222222222222222222222222222222222222222213434342213433434444344444344444333334333334344343434343434344444444443444444433444432100000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000001222222222222222222222222222222222222222223434343223433343333333333333333333333333333344333433333433333333333343333333333443432210000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000011122222222222222222222222222222244443244334334323433342222222222222223433334333334344333434333433332222222344322222343333434310000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000011122222222222222222222222222222233342234334444323444442222222222222223444444444444444344444344444332222222344322222344444444310000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000001111122222222222112223333333333344343334343222222222222222222222222222222222222222222222222222222343333332222221111111122222220000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000111122222222221111223443444444444334444442122222222222222222222222222222222222222222222222222211244444442222221111111112222220000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000111122222222221112223433443333344333333332222222222222222222222222222222222222222222222222222222333333332222111111111111111110000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000111111111111122223433443434333434322222222222222222244444442222222222222222222222222222223444322221111111000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000111111111111122223443443444343434322222222222222222243333342222222222222222222222222222223434322221111111000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011112222222222211111111111111111111111111122222222222222222222222222222222221111111110000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111222222222111111111111111111111111111122222222222222222222222222222222221111111110000000000000000000000000000000000000000000000000000000000000000000"; + + return rawMatrix + .Split('\n') + .Where(line => !string.IsNullOrEmpty(line)) + .Select(row => row.Select(ch => ch - '0').ToArray()) + .ToArray(); + } // Called when component is initialized protected override void OnInitialized() @@ -336,6 +423,12 @@ showDescription = !showDescription; } + private void ToggleAI() + { + enableAI = !enableAI; + statusMessage = enableAI ? "AI analysis enabled" : "AI analysis disabled"; + } + private async void ToggleJsonDisplay() { showJson = !showJson; @@ -369,7 +462,7 @@ // Log to browser console for debugging await JSRuntime.InvokeVoidAsync("console.log", "Checking IP: " + ipAddress); - abuseIpResult = await AbuseIPDBService.CheckIPAsync(ipAddress, 30, true); + abuseIpResult = await AbuseIPDBService.CheckIPAsync(ipAddress, 30, true, enableAI); // Format the JSON for display var options = new JsonSerializerOptions { WriteIndented = true }; diff --git a/README.md b/README.md index b3b95e8..429049f 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # Albatross -A modern Blazor WebAssembly application that provides comprehensive IP address analysis and abuse checking functionality. Albatross combines cloud IP range detection with AbuseIPDB reputation checking through a secure Cloudflare Worker proxy. +A modern Blazor WebAssembly application that provides comprehensive IP address analysis and abuse checking functionality. Albatross combines cloud IP range detection with AbuseIPDB reputation checking and AI-powered risk assessment through a secure Cloudflare Worker proxy. ## Features - **IP Abuse Checking**: Query the AbuseIPDB API to check if an IP address has been reported for malicious activity +- **AI-Powered Reputation Analysis**: Advanced risk assessment using Cloudflare Workers AI with Llama 3.1 70B Instruct model - **Cloud IP Range Detection**: Identify if an IP address belongs to major cloud providers (AWS, Azure, GCP, Oracle Cloud) - **Flexible Input Format**: Support for IP addresses with custom report age (e.g., `8.8.8.8;60` for 60 days of history) -- **Combined Data Sources**: Integrated AbuseIPDB and Cloudflare Radar API data for comprehensive IP analysis +- **Combined Data Sources**: Integrated AbuseIPDB, Cloudflare Radar API, and Workers AI for comprehensive IP analysis - **Secure Authentication**: Build-time generated HMAC authentication with timestamp validation for enhanced security - **CORS Protection**: Cloudflare Worker proxy handles CORS and protects API keys from client exposure - **SEO-Optimized**: Static HTML prerendering for improved search engine indexing and web crawler accessibility @@ -317,9 +318,41 @@ Users can specify custom report age limits using a semicolon delimiter: ### Combined API Integration - **AbuseIPDB**: IP reputation and abuse reports - **Cloudflare Radar**: ASN information and network details -- **Parallel Processing**: Both APIs are queried simultaneously for optimal performance +- **Cloudflare Workers AI**: AI-powered risk assessment using Llama 3.1 70B Instruct model +- **Optimized Processing**: AbuseIPDB and Radar APIs are queried in parallel; AI analysis processes their results - **Graceful Degradation**: Partial results if one API fails +### AI-Powered Reputation Analysis +The worker uses Cloudflare Workers AI with the Llama 3.1 70B Instruct model to generate intelligent risk assessments: +- **Real-time Analysis**: Analyzes data from AbuseIPDB and Cloudflare Radar APIs +- **Risk Levels**: Categorizes threats as low, medium, high, or critical +- **Trust Scores**: Provides 0-100 trust score for quick assessment +- **AI Summary**: Natural language explanation of the IP's reputation +- **Event Pattern Analysis**: Summarizes abuse event types and patterns from historical reports +- **Actionable Recommendations**: Specific steps to take based on the analysis +- **Model**: `@cf/meta/llama-3.1-70b-instruct` (70 billion parameter model) + +Example AI Response: +```json +{ + "aiReputation": { + "success": true, + "analysis": { + "riskLevel": "medium", + "trustScore": 65, + "summary": "This IP from US shows moderate abuse activity with 15 reports. ISP indicates datacenter usage which is common for both legitimate and malicious traffic.", + "eventsSummary": "Reported 15 times for Port Scan, Brute-Force, SSH activity over the past 30 days.", + "recommendations": [ + "Review the specific abuse reports for patterns", + "Consider rate limiting if used for API access" + ] + }, + "model": "@cf/meta/llama-3.1-70b-instruct", + "timestamp": "2025-10-28T04:45:00.000Z" + } +} +``` + ## Current Status ✅ **Authentication System**: Fully implemented and working @@ -329,6 +362,7 @@ Users can specify custom report age limits using a semicolon delimiter: ✅ **GitHub Actions**: Automated CI/CD pipeline functional ✅ **Security Scanning**: CodeQL and dependency review workflows active ✅ **Code Quality**: Automated formatting and testing in CI/CD +✅ **AI Integration**: Cloudflare Workers AI with Llama 3.1 70B for reputation analysis ## Image Credits diff --git a/Services/AbuseIPDBService.cs b/Services/AbuseIPDBService.cs index 9dcc224..efab371 100644 --- a/Services/AbuseIPDBService.cs +++ b/Services/AbuseIPDBService.cs @@ -13,10 +13,13 @@ namespace Albatross.Services { /// - /// Complete API response model for AbuseIPDB with integrated ASN information + /// Complete API response model for AbuseIPDB with integrated ASN information and AI reputation /// public class AbuseIPDBApiResponse { + [JsonPropertyName("aiReputation")] + public AIReputation? AIReputation { get; set; } + [JsonPropertyName("data")] public AbuseIPDBData? Data { get; set; } @@ -30,6 +33,48 @@ public class AbuseIPDBApiResponse public WorkerInfo? WorkerInfo { get; set; } } + /// + /// AI-generated reputation analysis from Cloudflare Workers AI + /// + public class AIReputation + { + [JsonPropertyName("success")] + public bool Success { get; set; } + + [JsonPropertyName("error")] + public string? Error { get; set; } + + [JsonPropertyName("analysis")] + public AIAnalysis? Analysis { get; set; } + + [JsonPropertyName("model")] + public string? Model { get; set; } + + [JsonPropertyName("timestamp")] + public string? Timestamp { get; set; } + } + + /// + /// AI analysis details + /// + public class AIAnalysis + { + [JsonPropertyName("riskLevel")] + public string? RiskLevel { get; set; } + + [JsonPropertyName("trustScore")] + public int TrustScore { get; set; } + + [JsonPropertyName("summary")] + public string? Summary { get; set; } + + [JsonPropertyName("eventsSummary")] + public string? EventsSummary { get; set; } + + [JsonPropertyName("recommendations")] + public List? Recommendations { get; set; } + } + /// /// ASN information from Cloudflare Radar API /// @@ -385,8 +430,9 @@ private string GetTimestamp() /// The IP address to check, optionally with maxAgeInDays delimited by semicolon (e.g., "8.8.8.8;60") /// Reports older than this many days won't be included (default 30). This is overridden if specified in ipAddress parameter /// Whether to include detailed report information + /// Whether to enable AI reputation analysis (default true) /// Complete AbuseIPDB information for the specified IP address - public async Task CheckIPAsync(string ipAddress, int maxAgeInDays = 30, bool verbose = true) + public async Task CheckIPAsync(string ipAddress, int maxAgeInDays = 30, bool verbose = true, bool enableAI = true) { // Parse the input to extract IP address and optionally maxAgeInDays string actualIpAddress; @@ -417,11 +463,12 @@ public async Task CheckIPAsync(string ipAddress, int maxAg try { var verboseParam = verbose.ToString().ToLower(); + var enableAIParam = enableAI.ToString().ToLower(); var timestamp = GetTimestamp(); // Include timestamp as a URI parameter var requestUrl = - $"{_cloudflareWorkerUrl}?ipAddress={actualIpAddress}&maxAgeInDays={actualMaxAgeInDays}&verbose={verboseParam}×tamp={timestamp}" + $"{_cloudflareWorkerUrl}?ipAddress={actualIpAddress}&maxAgeInDays={actualMaxAgeInDays}&verbose={verboseParam}&enableAI={enableAIParam}×tamp={timestamp}" .ToLower(); Console.WriteLine($"Requesting: {requestUrl}"); diff --git a/cloudflare-worker.template.js b/cloudflare-worker.template.js index 9334a85..1d3f8d5 100644 --- a/cloudflare-worker.template.js +++ b/cloudflare-worker.template.js @@ -4,11 +4,13 @@ * This worker provides comprehensive IP analysis by combining multiple data sources: * 1. AbuseIPDB API - IP abuse reputation checking * 2. Cloudflare Radar API - ASN and network information lookup + * 3. Cloudflare Workers AI - AI-powered reputation analysis using Llama 3.1 70B * * Features: * - Protects API keys from client exposure * - Handles CORS for browser requests - * - Fetches both APIs in parallel for optimal performance + * - Fetches multiple APIs in parallel for optimal performance + * - Generates AI-powered risk assessments and recommendations * - Returns combined data in a single JSON response * - Uses HMAC-based authentication for security with build-time generated keys * - Provides fallback data when one API fails @@ -24,6 +26,18 @@ * "data": [ ... Cloudflare Radar ASN data ... ], * "error": null/string * }, + * "aiReputation": { + * "success": true/false, + * "error": null/string, + * "analysis": { + * "riskLevel": "low|medium|high|critical", + * "trustScore": <0-100>, + * "summary": "", + * "recommendations": ["", ""] + * }, + * "model": "@cf/meta/llama-3.1-70b-instruct", + * "timestamp": "" + * }, * "workerInfo": { ... metadata ... } * } */ @@ -85,21 +99,164 @@ const ALLOWED_ORIGINS = [ "http://localhost:5044" ]; -addEventListener('fetch', event => { - event.respondWith(handleRequest(event.request)); -}); +// ES Module export for Cloudflare Workers (required for AI binding) +export default { + async fetch(request, env, ctx) { + // Handle CORS preflight requests + if (request.method === "OPTIONS") { + return handleCORS(request); + } -async function handleRequest(request) { - // Handle CORS preflight requests - if (request.method === "OPTIONS") { - return handleCORS(request); + // All requests go through the combined handler + return handleCombinedRequest(request, env); } +}; - // All requests go through the combined handler - return handleCombinedRequest(request); +/** + * Generate AI-based IP reputation analysis using Cloudflare Workers AI + * @param {object} env - Worker environment with AI binding + * @param {string} ipAddress - The IP address to analyze + * @param {object} abuseData - AbuseIPDB data for context + * @param {object} asnData - ASN data for context + * @returns {Promise} AI reputation analysis + */ +async function generateAIReputation(env, ipAddress, abuseData, asnData) { + try { + // Check if AI binding is available + if (!env || !env.AI) { + console.log('AI binding not available in environment'); + return { + success: false, + error: 'AI service not available', + analysis: null + }; + } + + // Build context from available data + const abuseScore = abuseData?.data?.abuseConfidenceScore || 0; + const totalReports = abuseData?.data?.totalReports || 0; + const countryCode = abuseData?.data?.countryCode || 'Unknown'; + const isp = abuseData?.data?.isp || 'Unknown'; + const usageType = abuseData?.data?.usageType || 'Unknown'; + const asnName = asnData?.result?.[0]?.asn?.name || 'Unknown'; + const asnNumber = asnData?.result?.[0]?.asn?.asn || 'Unknown'; + const numDistinctUsers = abuseData?.data?.numDistinctUsers || 0; + const reports = abuseData?.data?.reports || []; + + // Extract sample abuse event details + let eventSummary = ''; + if (reports.length > 0) { + const recentReports = reports.slice(0, 5); + const categories = recentReports.flatMap(r => r.categories || []); + const uniqueCategories = [...new Set(categories)]; + const comments = recentReports.map(r => r.comment).filter(c => c && c.trim()); + + eventSummary = `\n\nAbuse Events (${totalReports} reports from ${numDistinctUsers} users):`; + if (uniqueCategories.length > 0) { + eventSummary += `\nEvent Types: ${uniqueCategories.join(', ')}`; + } + if (comments.length > 0) { + eventSummary += `\nSample Reports:\n${comments.slice(0, 3).map((c, i) => ` ${i + 1}. ${c.substring(0, 100)}${c.length > 100 ? '...' : ''}`).join('\n')}`; + } + } + + // Create a prompt for the AI to analyze the IP reputation + const prompt = `You are a cybersecurity expert analyzing IP address reputation. Based on the following real-time data, provide a concise risk assessment and reputation summary. + +IP Address: ${ipAddress} +Country: ${countryCode} +ISP: ${isp} +Usage Type: ${usageType} +ASN: ${asnNumber} (${asnName}) +Abuse Confidence Score: ${abuseScore}% (0-100 scale, higher is worse) +Total Abuse Reports: ${totalReports}${eventSummary} + +Provide a JSON response with the following structure: +{ + "riskLevel": "low|medium|high|critical", + "trustScore": , + "summary": "<2-3 sentence overall risk assessment>", + "eventsSummary": "<2-3 sentence summary of abuse event patterns & targeted services or null if no events>", + "recommendations": ["", ""] } -async function handleCombinedRequest(request) { +Focus on actionable insights based on the abuse score, report count, network information, and abuse event patterns. Keep summaries concise and professional.`; + + console.log('Calling Workers AI with Llama 3.1 70B for IP reputation analysis...'); + + // Call Cloudflare Workers AI using Llama 3.1 70B Instruct model + const response = await env.AI.run('@cf/meta/llama-3.1-70b-instruct', { + messages: [ + { + role: 'system', + content: 'You are a cybersecurity expert. Respond only with valid JSON, no markdown formatting or code blocks.' + }, + { + role: 'user', + content: prompt + } + ], + max_tokens: 500, + temperature: 0.3, // Lower temperature for more consistent, factual responses + }); + + console.log('AI response received:', response); + + // Parse the AI response + let analysis = null; + if (response && response.response) { + try { + // The response.response contains the AI's text output + const aiText = response.response.trim(); + + // Try to extract JSON from the response (handle cases where AI might add extra text) + let jsonText = aiText; + if (aiText.includes('{')) { + const startIdx = aiText.indexOf('{'); + const endIdx = aiText.lastIndexOf('}'); + if (startIdx >= 0 && endIdx > startIdx) { + jsonText = aiText.substring(startIdx, endIdx + 1); + } + } + + analysis = JSON.parse(jsonText); + console.log('AI analysis parsed successfully:', analysis); + } catch (parseError) { + console.error('Error parsing AI response:', parseError); + // Fallback to basic analysis if JSON parsing fails + const eventsFallback = reports.length > 0 + ? `Reported ${totalReports} times for ${[...new Set(reports.flatMap(r => r.categories || []))].slice(0, 3).join(', ')}.` + : null; + + analysis = { + riskLevel: abuseScore > 75 ? 'critical' : abuseScore > 50 ? 'high' : abuseScore > 25 ? 'medium' : 'low', + trustScore: Math.max(0, 100 - abuseScore), + summary: `IP from ${countryCode} with ${abuseScore}% abuse confidence score and ${totalReports} reports.`, + eventsSummary: eventsFallback, + recommendations: ['Review the abuse reports for details', 'Consider blocking if risk level is high'] + }; + } + } + + return { + success: true, + error: null, + analysis: analysis, + model: '@cf/meta/llama-3.1-70b-instruct', + timestamp: new Date().toISOString() + }; + + } catch (error) { + console.error('AI reputation generation error:', error); + return { + success: false, + error: error.message || 'Failed to generate AI reputation', + analysis: null + }; + } +} + +async function handleCombinedRequest(request, env) { // Get the request origin const origin = request.headers.get('Origin') || ''; @@ -242,9 +399,10 @@ async function handleCombinedRequest(request) { const ipAddress = url.searchParams.get('ipaddress'); // lowercase parameter name const maxAgeInDays = url.searchParams.get('maxageindays') || 30; // lowercase parameter name const verbose = url.searchParams.get('verbose') === 'true'; + const enableAI = url.searchParams.get('enableai') === 'true'; // AI toggle parameter // Debug: Log the parsed parameters - console.log('Parsed parameters:', { ipAddress, maxAgeInDays, verbose }); + console.log('Parsed parameters:', { ipAddress, maxAgeInDays, verbose, enableAI }); // Validate required parameters if (!ipAddress) { @@ -336,8 +494,30 @@ async function handleCombinedRequest(request) { radarError = `Cloudflare Radar API error: ${status} ${statusText}`; } + // Generate AI-based reputation analysis using the collected data (only if enabled) + let aiReputation = null; + if (enableAI) { + console.log('Generating AI reputation analysis...'); + aiReputation = await generateAIReputation(env, ipAddress, abuseIPDBData, radarData); + console.log('AI reputation analysis result:', { + success: aiReputation.success, + error: aiReputation.error + }); + } else { + console.log('AI reputation analysis disabled by client request'); + aiReputation = { + success: false, + error: 'AI analysis disabled', + analysis: null + }; + } + // Combine the responses into a single response object const combinedResponse = { + + // Add AI reputation analysis + aiReputation: aiReputation, + // AbuseIPDB data (maintain original structure for compatibility) data: abuseIPDBData?.data || null, @@ -356,7 +536,8 @@ async function handleCombinedRequest(request) { requestId: generateRequestId(), sources: { abuseipdb: abuseIPDBError ? 'error' : 'success', - radar: radarError ? 'error' : 'success' + radar: radarError ? 'error' : 'success', + ai: enableAI ? (aiReputation.success ? 'success' : 'error') : 'disabled' } } }; diff --git a/wrangler.toml b/wrangler.toml index 9e4d61f..6c9bd23 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -7,6 +7,10 @@ compatibility_flags = ["nodejs_compat"] [observability.logs] enabled = true +# AI binding for Workers AI +[ai] +binding = "AI" + # Production environment (default) [env.production] name = "abuseipdb" diff --git a/wwwroot/css/app.css b/wwwroot/css/app.css index 59dadae..5e784d5 100644 --- a/wwwroot/css/app.css +++ b/wwwroot/css/app.css @@ -304,13 +304,14 @@ code { width: 500px; max-width: 90vw; font-family: 'Quantico', monospace; - font-size: 1rem; + font-size: 0.8rem; padding: 8px 12px; border: 2px solid #dee2e6; border-radius: 4px; margin-right: 10px; margin-bottom: 20px; transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + text-align: center; } .ip-input:focus { @@ -322,6 +323,7 @@ code { .ip-input::placeholder { color: #6c757d; opacity: 1; + text-align: center; } /* Button spacing */ @@ -394,10 +396,9 @@ pre { padding-left: 2rem; padding-right: 2rem; } - /* Scale down input and button sizes for wide screens */ + /* Scale input width on wide screens */ .ip-input { width: 450px; - font-size: 0.95rem; } .btn { @@ -437,10 +438,9 @@ pre { .content { max-width: 1320px; } - /* Further scale down for very wide screens */ + /* Further scale input width for very wide screens */ .ip-input { width: 420px; - font-size: 0.9rem; } .btn { @@ -478,10 +478,9 @@ pre { .content { max-width: 1400px; } - /* Even more conservative for 4K and ultra-wide monitors */ + /* Scale input width for 4K and ultra-wide monitors */ .ip-input { width: 400px; - font-size: 0.85rem; } .card-container { @@ -1212,3 +1211,63 @@ html body div.card div.card-header .btn { } } +/* Pixel matrix styles for ASCII art replacement */ +.ascii-art-pixel-matrix { + display: inline-block; + margin: 0 auto; + padding: 5px 0 0 5px; + width: 50%; + max-width: 1163px; +} + +.pixel-matrix__grid { + display: grid; + grid-template-columns: repeat(240, 1fr); + grid-auto-rows: auto; + aspect-ratio: 1163 / 216; + width: 100%; + gap: 0; + background: white; + border: none; +} + +.pixel-matrix__pixel { + display: block; + width: 100%; + height: 100%; + aspect-ratio: 1 / 1; +} + +.pixel-matrix__pixel--empty { + background-color: #ffffff; +} + +.pixel-matrix__pixel--edge { + background-color: #cccccc; +} + +.pixel-matrix__pixel--mid { + background-color: #888888; +} + +.pixel-matrix__pixel--dark { + background-color: #333333; +} + +.pixel-matrix__pixel--core { + background-color: #000000; +} + +/* Responsive sizing for pixel matrix */ +@media (max-width: 768px) { + .ascii-art-pixel-matrix { + width: 70%; + } +} + +@media (max-width: 480px) { + .ascii-art-pixel-matrix { + width: 90%; + } +} + diff --git a/wwwroot/index.html b/wwwroot/index.html index 56e82c7..a407573 100644 --- a/wwwroot/index.html +++ b/wwwroot/index.html @@ -4,11 +4,45 @@ - Albatross + Albatross - Cloud IP Analysis & Reputation Checker + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + diff --git a/wwwroot/sitemap.xml b/wwwroot/sitemap.xml index e69de29..debbbd6 100644 --- a/wwwroot/sitemap.xml +++ b/wwwroot/sitemap.xml @@ -0,0 +1,48 @@ + + + + + + https://albatross.devnomadic.com/ + 2025-10-31 + weekly + 1.0 + + + https://albatross.devnomadic.com/ip-manifests/AWS.json + 2025-10-31 + daily + 0.8 + + + https://albatross.devnomadic.com/ip-manifests/Azure.json + 2025-10-31 + daily + 0.8 + + + https://albatross.devnomadic.com/ip-manifests/GCP.json + 2025-10-31 + daily + 0.8 + + + https://albatross.devnomadic.com/ip-manifests/Oracle.json + 2025-10-31 + daily + 0.8 + + + https://albatross.devnomadic.com/robots.txt + 2025-10-31 + monthly + 0.3 + + + + + +