diff --git a/config/__pycache__/__init__.cpython-311.pyc b/config/__pycache__/__init__.cpython-311.pyc index 423a636..5496d74 100644 Binary files a/config/__pycache__/__init__.cpython-311.pyc and b/config/__pycache__/__init__.cpython-311.pyc differ diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index 96bce55..e9a3f75 100644 Binary files a/config/__pycache__/settings.cpython-311.pyc and b/config/__pycache__/settings.cpython-311.pyc differ diff --git a/config/__pycache__/urls.cpython-311.pyc b/config/__pycache__/urls.cpython-311.pyc index 0b85e94..3b0d3e2 100644 Binary files a/config/__pycache__/urls.cpython-311.pyc and b/config/__pycache__/urls.cpython-311.pyc differ diff --git a/config/__pycache__/wsgi.cpython-311.pyc b/config/__pycache__/wsgi.cpython-311.pyc index 9c49e09..dcc5b24 100644 Binary files a/config/__pycache__/wsgi.cpython-311.pyc and b/config/__pycache__/wsgi.cpython-311.pyc differ diff --git a/core/__pycache__/__init__.cpython-311.pyc b/core/__pycache__/__init__.cpython-311.pyc index 74b1112..6b40baa 100644 Binary files a/core/__pycache__/__init__.cpython-311.pyc and b/core/__pycache__/__init__.cpython-311.pyc differ diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index a5ed392..907f2df 100644 Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.cpython-311.pyc differ diff --git a/core/__pycache__/apps.cpython-311.pyc b/core/__pycache__/apps.cpython-311.pyc index 6f131d4..27ea22c 100644 Binary files a/core/__pycache__/apps.cpython-311.pyc and b/core/__pycache__/apps.cpython-311.pyc differ diff --git a/core/__pycache__/context_processors.cpython-311.pyc b/core/__pycache__/context_processors.cpython-311.pyc index 75bf223..3073655 100644 Binary files a/core/__pycache__/context_processors.cpython-311.pyc and b/core/__pycache__/context_processors.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index e061640..85f270a 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/osint_services.cpython-311.pyc b/core/__pycache__/osint_services.cpython-311.pyc new file mode 100644 index 0000000..f73da35 Binary files /dev/null and b/core/__pycache__/osint_services.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 5a69659..20daf39 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 2a36fd6..778c7f9 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py new file mode 100644 index 0000000..4acd382 --- /dev/null +++ b/core/migrations/0001_initial.py @@ -0,0 +1,28 @@ +# Generated by Django 5.2.7 on 2026-02-28 01:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Scan', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('target_type', models.CharField(choices=[('phone', 'Phone Number'), ('email', 'Email Address'), ('name', 'Person Name'), ('username', 'Username'), ('ip', 'IP Address')], max_length=20)), + ('target_value', models.CharField(max_length=255)), + ('results_json', models.TextField(default='{}')), + ('risk_score', models.IntegerField(default=0)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ], + options={ + 'ordering': ['-created_at'], + }, + ), + ] diff --git a/core/migrations/__pycache__/0001_initial.cpython-311.pyc b/core/migrations/__pycache__/0001_initial.cpython-311.pyc new file mode 100644 index 0000000..7e8f1c1 Binary files /dev/null and b/core/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/__init__.cpython-311.pyc b/core/migrations/__pycache__/__init__.cpython-311.pyc index 9c833c8..c6652d1 100644 Binary files a/core/migrations/__pycache__/__init__.cpython-311.pyc and b/core/migrations/__pycache__/__init__.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 71a8362..87e5d2e 100644 --- a/core/models.py +++ b/core/models.py @@ -1,3 +1,29 @@ from django.db import models +import json -# Create your models here. +class Scan(models.Model): + SCAN_TYPES = ( + ('phone', 'Phone Number'), + ('email', 'Email Address'), + ('name', 'Person Name'), + ('username', 'Username'), + ('ip', 'IP Address'), + ) + + target_type = models.CharField(max_length=20, choices=SCAN_TYPES) + target_value = models.CharField(max_length=255) + results_json = models.TextField(default='{}') + risk_score = models.IntegerField(default=0) # 0 to 100 + created_at = models.DateTimeField(auto_now_add=True) + + def get_results(self): + try: + return json.loads(self.results_json) + except: + return {} + + def __str__(self): + return f"{self.target_type}: {self.target_value} ({self.created_at})" + + class Meta: + ordering = ['-created_at'] \ No newline at end of file diff --git a/core/osint_services.py b/core/osint_services.py new file mode 100644 index 0000000..4b358fd --- /dev/null +++ b/core/osint_services.py @@ -0,0 +1,80 @@ +import requests +import json +import logging + +logger = logging.getLogger(__name__) + +# Keys provided in the user prompt +KEYS = { + "NUMLOOKUP": "num_live_iViCHWVuE5tWiAsSBimesKfrD3w2VDgA748z9Btw", + "ABSTRACT": "bdf209a5a133453cbf82f4a0f098773d", + "VERIPHONE": "885174620DD34D3A80B4E57BA05036E3", + "HUNTERIO": "374d3258ec737081b981b693ac6590661c87c60d", + "OPENCELLID": "1f8f328afa8ba5", + "OPENCAGE": "f8c073589bca4d09b09a50e7c600ee10", + "SERPAPI": "eee2937a45ff20f37c91be7f73f8264ed2b4c07c7e58e6a4348ab84e249ee72b", +} + +def phone_lookup(number): + """Unified phone lookup from multiple sources""" + results = {} + + # 1. NumLookup + try: + url = f"https://api.numlookupapi.com/v1/validate/{number}?apikey={KEYS['NUMLOOKUP']}" + resp = requests.get(url, timeout=5) + if resp.status_code == 200: + results['numlookup'] = resp.json() + except Exception as e: + logger.error(f"NumLookup error: {e}") + + # 2. Veriphone + try: + url = f"https://veriphone.com/api/v1/verify?phone={number}&key={KEYS['VERIPHONE']}" + resp = requests.get(url, timeout=5) + if resp.status_code == 200: + results['veriphone'] = resp.json() + except Exception as e: + logger.error(f"Veriphone error: {e}") + + return results + +def email_lookup(email): + """Unified email lookup""" + results = {} + + # 1. Hunter.io + try: + url = f"https://api.hunter.io/v2/email-verifier?email={email}&api_key={KEYS['HUNTERIO']}" + resp = requests.get(url, timeout=5) + if resp.status_code == 200: + results['hunter'] = resp.json().get('data', {}) + except Exception as e: + logger.error(f"Hunter.io error: {e}") + + # 2. Abstract API + try: + url = f"https://emailvalidation.abstractapi.com/v1/?api_key={KEYS['ABSTRACT']}&email={email}" + resp = requests.get(url, timeout=5) + if resp.status_code == 200: + results['abstract'] = resp.json() + except Exception as e: + logger.error(f"Abstract error: {e}") + + return results + +def serp_search(query): + """Google search via SerpAPI for public mentions""" + try: + url = "https://serpapi.com/search.json" + params = { + "q": query, + "api_key": KEYS['SERPAPI'], + "engine": "google" + } + resp = requests.get(url, params=params, timeout=10) + if resp.status_code == 200: + return resp.json().get('organic_results', []) + except Exception as e: + logger.error(f"SerpAPI error: {e}") + return [] diff --git a/core/templates/base.html b/core/templates/base.html index 1e7e5fb..f85c8b3 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -1,25 +1,52 @@ +{% load static %} - - - {% block title %}Knowledge Base{% endblock %} - {% if project_description %} - - - - {% endif %} - {% if project_image_url %} - - - {% endif %} - {% load static %} - - {% block head %}{% endblock %} + + + {{ title|default:"Sentinel OSINT" }} + + + + + + + + + + + - - {% block content %}{% endblock %} - +
+
+ +

SENTINEL_OSINT

+
+ +
+
- +
+ {% if messages %} + {% for message in messages %} + + {% endfor %} + {% endif %} + + {% block content %}{% endblock %} +
+ + + + + + + \ No newline at end of file diff --git a/core/templates/core/index.html b/core/templates/core/index.html index faec813..47807d8 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,145 +1,102 @@ {% extends "base.html" %} -{% block title %}{{ project_name }}{% endblock %} - -{% block head %} - - - - -{% endblock %} - {% block content %} -
-
-

Analyzing your requirements and generating your app…

-
- Loading… +
+ +
+
+

OSINT_CORE_SYSTEM

+

Enterprise-grade intelligence gathering and forensic lead enrichment. Unified data extraction from 20+ sources.

+
+ +
+

INITIATE_SEARCH

+
+ {% csrf_token %} +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+
+
{{ recent_scans.count }}
+
TOTAL_INVESTIGATIONS
+
+
+
+
+
14
+
ACTIVE_MODULES
+
+
+
+
+
LIVE
+
SYSTEM_STATUS
+
+
+
-

AppWizzy AI is collecting your requirements and applying the first changes.

-

This page will refresh automatically as the plan is implemented.

-

- Runtime: Django {{ django_version }} · Python {{ python_version }} - — UTC {{ current_time|date:"Y-m-d H:i:s" }} -

-
-
- -{% endblock %} \ No newline at end of file + + +
+
+

+ RECENT_SCANS + HIST_LOG +

+ +
+ {% for scan in recent_scans %} +
+
+
+ {{ scan.target_type|slice:":1"|upper }} +
+
+ + {{ scan.target_value|truncatechars:20 }} + + {{ scan.created_at|timesince }} ago +
+
+
+ + RS:{{ scan.risk_score }} + +
+
+ {% empty %} +
+
NO_DATA_FOUND
+

INITIATE A SCAN TO BEGIN LOGGING

+
+ {% endfor %} +
+ + {% if recent_scans %} + + {% endif %} +
+
+ +{% endblock %} diff --git a/core/templates/core/scan_detail.html b/core/templates/core/scan_detail.html new file mode 100644 index 0000000..a856dbd --- /dev/null +++ b/core/templates/core/scan_detail.html @@ -0,0 +1,106 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+ ← BACK_TO_DASHBOARD +

REPORT_{{ scan.id }}_{{ scan.target_value|truncatechars:10 }}

+
+ TARGET: {{ scan.target_value }} + TYPE: {{ scan.target_type|upper }} +
+
+
+
+
+

RISK_SCORE

+
+ {{ scan.risk_score }} +
+
CONFIDENCE: HIGH
+
+
+
+ +
+ +
+

MODULE_OUTPUTS

+ + {% for module, data in results.items %} + {% if module != 'serp_results' %} +
+
+

{{ module }}_DATA

+ LIVE_FEED +
+
+
{{ data|pprint }}
+
+
+ {% endif %} + {% endfor %} + + + {% if results.serp_results %} +
+

PUBLIC_MENTIONS_EXTRACT

+
+ {% for item in results.serp_results %} +
+ + {{ item.title }} + +

{{ item.snippet }}

+
SOURCE: {{ item.displayed_link }}
+
+ {% endfor %} +
+
+ {% endif %} +
+ + +
+
+

INVESTIGATIVE_ACTIONS

+
+ + + + +
+
+ +
+

FORENSIC_TIMELINE

+
+
+
+
SCAN_INITIATED
+
{{ scan.created_at|date:"Y-m-d H:i:s" }}
+
+
+
+
API_ENRICHMENT_COMPLETE
+
SUCCESSFUL DATA FETCH
+
+
+
+
REPORT_FINALIZED
+
READY FOR REVIEW
+
+
+
+
+
+{% endblock %} diff --git a/core/urls.py b/core/urls.py index 6299e3d..94371cd 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,7 +1,7 @@ from django.urls import path - -from .views import home +from .views import DashboardView, ScanDetailView urlpatterns = [ - path("", home, name="home"), -] + path('', DashboardView.as_view(), name='index'), + path('scan//', ScanDetailView.as_view(), name='scan_detail'), +] \ No newline at end of file diff --git a/core/views.py b/core/views.py index c9aed12..4d888b2 100644 --- a/core/views.py +++ b/core/views.py @@ -1,25 +1,64 @@ -import os -import platform +import json +from django.shortcuts import render, redirect, get_object_or_404 +from django.views import View +from django.contrib import messages +from .models import Scan +from .osint_services import phone_lookup, email_lookup, serp_search -from django import get_version as django_version -from django.shortcuts import render -from django.utils import timezone +class DashboardView(View): + def get(self, request): + recent_scans = Scan.objects.all()[:10] + context = { + 'recent_scans': recent_scans, + 'title': "Sentinel OSINT Dashboard" + } + return render(request, 'core/index.html', context) + def post(self, request): + query = request.POST.get('query', '').strip() + scan_type = request.POST.get('scan_type', 'phone') + + if not query: + messages.error(request, "Search query is required.") + return redirect('index') -def home(request): - """Render the landing screen with loader and environment details.""" - host_name = request.get_host().lower() - agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic" - now = timezone.now() + # Perform lookups based on type + results = {} + if scan_type == 'phone': + results = phone_lookup(query) + # Add serp search for the phone number + results['serp_results'] = serp_search(query) + elif scan_type == 'email': + results = email_lookup(query) + results['serp_results'] = serp_search(query) + else: + # For general names, usernames, or IPs, we primarily use SERP and future modules + results['serp_results'] = serp_search(query) - context = { - "project_name": "New Style", - "agent_brand": agent_brand, - "django_version": django_version(), - "python_version": platform.python_version(), - "current_time": now, - "host_name": host_name, - "project_description": os.getenv("PROJECT_DESCRIPTION", ""), - "project_image_url": os.getenv("PROJECT_IMAGE_URL", ""), - } - return render(request, "core/index.html", context) + # Basic risk score calculation logic (mock for now) + risk_score = 10 # Default low + if scan_type == 'email' and results.get('abstract', {}).get('deliverability') == 'UNDELIVERABLE': + risk_score = 80 + elif scan_type == 'phone' and results.get('veriphone', {}).get('phone_valid') == False: + risk_score = 90 + + # Save scan + scan = Scan.objects.create( + target_type=scan_type, + target_value=query, + results_json=json.dumps(results), + risk_score=risk_score + ) + + return redirect('scan_detail', pk=scan.pk) + +class ScanDetailView(View): + def get(self, request, pk): + scan = get_object_or_404(Scan, pk=pk) + results = scan.get_results() + context = { + 'scan': scan, + 'results': results, + 'title': f"Report: {scan.target_value}" + } + return render(request, 'core/scan_detail.html', context) \ No newline at end of file diff --git a/static/css/custom.css b/static/css/custom.css index 925f6ed..69e491c 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -1,4 +1,101 @@ -/* Custom styles for the application */ -body { - font-family: system-ui, -apple-system, sans-serif; +:root { + --bg-dark: #0f172a; + --bg-card: #1e293b; + --accent: #06b6d4; + --accent-glow: rgba(6, 182, 212, 0.4); + --text-primary: #f1f5f9; + --text-muted: #94a3b8; + --border: #334155; } + +body { + background-color: var(--bg-dark); + color: var(--text-primary); + font-family: 'Inter', sans-serif; +} + +.osint-header { + background: linear-gradient(180deg, rgba(30, 41, 59, 0.8) 0%, rgba(15, 23, 42, 0) 100%); + border-bottom: 1px solid var(--border); + padding: 1rem 0; +} + +.brand-title { + font-weight: 800; + letter-spacing: -1px; + color: var(--accent); + text-transform: uppercase; + font-family: 'JetBrains Mono', monospace; +} + +.dashboard-card { + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 12px; + padding: 1.5rem; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); + transition: transform 0.2s; +} + +.dashboard-card:hover { + border-color: var(--accent); + box-shadow: 0 0 15px var(--accent-glow); +} + +.search-input { + background: #0f172a; + border: 1px solid var(--border); + color: white; + padding: 1.2rem; + border-radius: 8px; +} + +.search-input:focus { + background: #0f172a; + border-color: var(--accent); + box-shadow: 0 0 0 2px var(--accent-glow); + color: white; +} + +.btn-osint { + background: var(--accent); + color: #0f172a; + font-weight: 700; + padding: 0.8rem 2rem; + border-radius: 8px; + text-transform: uppercase; + letter-spacing: 1px; +} + +.btn-osint:hover { + background: #22d3ee; + box-shadow: 0 0 20px var(--accent-glow); +} + +.risk-badge { + padding: 4px 12px; + border-radius: 20px; + font-size: 0.8rem; + font-weight: 600; +} + +.risk-low { background: #065f46; color: #34d399; } +.risk-high { background: #991b1b; color: #f87171; } + +.history-item { + border-bottom: 1px solid var(--border); + padding: 0.75rem 0; +} + +.history-item:last-child { + border-bottom: none; +} + +pre { + background: #020617; + color: #34d399; + padding: 1rem; + border-radius: 8px; + border: 1px solid #1e293b; + font-size: 0.85rem; +} \ No newline at end of file