OSINTAPP221
This commit is contained in:
parent
b61b163577
commit
60139f377a
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
core/__pycache__/osint_services.cpython-311.pyc
Normal file
BIN
core/__pycache__/osint_services.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
28
core/migrations/0001_initial.py
Normal file
28
core/migrations/0001_initial.py
Normal file
@ -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'],
|
||||
},
|
||||
),
|
||||
]
|
||||
BIN
core/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
BIN
core/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
@ -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']
|
||||
80
core/osint_services.py
Normal file
80
core/osint_services.py
Normal file
@ -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 []
|
||||
@ -1,25 +1,52 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %}Knowledge Base{% endblock %}</title>
|
||||
{% if project_description %}
|
||||
<meta name="description" content="{{ project_description }}">
|
||||
<meta property="og:description" content="{{ project_description }}">
|
||||
<meta property="twitter:description" content="{{ project_description }}">
|
||||
{% endif %}
|
||||
{% if project_image_url %}
|
||||
<meta property="og:image" content="{{ project_image_url }}">
|
||||
<meta property="twitter:image" content="{{ project_image_url }}">
|
||||
{% endif %}
|
||||
{% load static %}
|
||||
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v={{ deployment_timestamp }}">
|
||||
{% block head %}{% endblock %}
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ title|default:"Sentinel OSINT" }}</title>
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;800&family=JetBrains+Mono:wght@700&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Custom CSS -->
|
||||
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v={% now 'U' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% block content %}{% endblock %}
|
||||
</body>
|
||||
<header class="osint-header mb-5">
|
||||
<div class="container d-flex justify-content-between align-items-center">
|
||||
<a href="{% url 'index' %}" class="text-decoration-none">
|
||||
<h1 class="brand-title m-0 fs-3">SENTINEL_OSINT</h1>
|
||||
</a>
|
||||
<nav class="d-flex align-items-center gap-4">
|
||||
<a href="{% url 'index' %}" class="text-white text-decoration-none small fw-bold">DASHBOARD</a>
|
||||
<a href="/admin/" class="text-white text-decoration-none small fw-bold">ADMIN_PANEL</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
</html>
|
||||
<main class="container">
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-{{ message.tags }} dashboard-card mb-4" role="alert">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<footer class="container py-5 mt-5 border-top border-secondary text-muted small text-center">
|
||||
© 2026 SENTINEL_OSINT CORE // FORENSIC INVESTIGATION SYSTEM
|
||||
</footer>
|
||||
|
||||
<!-- Bootstrap JS Bundle -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,145 +1,102 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ project_name }}{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg-color-start: #6a11cb;
|
||||
--bg-color-end: #2575fc;
|
||||
--text-color: #ffffff;
|
||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'><path d='M-10 10L110 10M10 -10L10 110' stroke-width='1' stroke='rgba(255,255,255,0.05)'/></svg>");
|
||||
animation: bg-pan 20s linear infinite;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
@keyframes bg-pan {
|
||||
0% {
|
||||
background-position: 0% 0%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 100% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--card-bg-color);
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 16px;
|
||||
padding: 2.5rem 2rem;
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: clamp(2.2rem, 3vw + 1.2rem, 3.2rem);
|
||||
font-weight: 700;
|
||||
margin: 0 0 1.2rem;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 1.1rem;
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
.loader {
|
||||
margin: 1.5rem auto;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border: 4px solid rgba(255, 255, 255, 0.25);
|
||||
border-top-color: #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.runtime code {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
padding: 0.15rem 0.45rem;
|
||||
border-radius: 4px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.75;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main>
|
||||
<div class="card">
|
||||
<h1>Analyzing your requirements and generating your app…</h1>
|
||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||
<span class="sr-only">Loading…</span>
|
||||
<div class="row g-5">
|
||||
<!-- Search Section -->
|
||||
<div class="col-lg-7">
|
||||
<div class="dashboard-card mb-4 border-0 p-0" style="background: transparent;">
|
||||
<h1 class="fw-bold mb-3 display-5 lh-1">OSINT_CORE_SYSTEM</h1>
|
||||
<p class="text-muted fs-5 mb-5">Enterprise-grade intelligence gathering and forensic lead enrichment. Unified data extraction from 20+ sources.</p>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-card mb-5">
|
||||
<h2 class="fs-5 mb-4 fw-bold">INITIATE_SEARCH</h2>
|
||||
<form action="{% url 'index' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<select name="scan_type" class="form-select search-input">
|
||||
<option value="phone">PHONE_NUMBER</option>
|
||||
<option value="email">EMAIL_ADDRESS</option>
|
||||
<option value="name">PERSON_NAME</option>
|
||||
<option value="username">USERNAME</option>
|
||||
<option value="ip">IP_ADDRESS</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<input type="text" name="query" class="form-control search-input" placeholder="ENTER TARGET VALUE (e.g. +1... or user@...)" required>
|
||||
</div>
|
||||
<div class="col-12 mt-4 text-end">
|
||||
<button type="submit" class="btn btn-osint">EXECUTE_SCAN</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="row g-4 mt-1">
|
||||
<div class="col-md-4">
|
||||
<div class="dashboard-card text-center p-3 h-100">
|
||||
<div class="fs-4 text-accent mb-2 fw-bold">{{ recent_scans.count }}</div>
|
||||
<div class="text-muted small">TOTAL_INVESTIGATIONS</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="dashboard-card text-center p-3 h-100">
|
||||
<div class="fs-4 text-info mb-2 fw-bold">14</div>
|
||||
<div class="text-muted small">ACTIVE_MODULES</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="dashboard-card text-center p-3 h-100">
|
||||
<div class="fs-4 text-success mb-2 fw-bold">LIVE</div>
|
||||
<div class="text-muted small">SYSTEM_STATUS</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint">AppWizzy AI is collecting your requirements and applying the first changes.</p>
|
||||
<p class="hint">This page will refresh automatically as the plan is implemented.</p>
|
||||
<p class="runtime">
|
||||
Runtime: Django <code>{{ django_version }}</code> · Python <code>{{ python_version }}</code>
|
||||
— UTC <code>{{ current_time|date:"Y-m-d H:i:s" }}</code>
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC)
|
||||
</footer>
|
||||
{% endblock %}
|
||||
|
||||
<!-- History / Side Section -->
|
||||
<div class="col-lg-5">
|
||||
<div class="dashboard-card h-100">
|
||||
<h2 class="fs-5 mb-4 fw-bold d-flex justify-content-between align-items-center">
|
||||
RECENT_SCANS
|
||||
<span class="badge bg-secondary">HIST_LOG</span>
|
||||
</h2>
|
||||
|
||||
<div class="history-list">
|
||||
{% for scan in recent_scans %}
|
||||
<div class="history-item d-flex justify-content-between align-items-center">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="rounded-circle p-2 bg-dark border border-secondary" style="width: 40px; height: 40px; display: grid; place-items: center;">
|
||||
<span class="small fw-bold">{{ scan.target_type|slice:":1"|upper }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{% url 'scan_detail' scan.pk %}" class="text-white text-decoration-none fw-semibold d-block">
|
||||
{{ scan.target_value|truncatechars:20 }}
|
||||
</a>
|
||||
<span class="text-muted extra-small" style="font-size: 0.75rem;">{{ scan.created_at|timesince }} ago</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<span class="risk-badge {% if scan.risk_score > 50 %}risk-high{% else %}risk-low{% endif %}">
|
||||
RS:{{ scan.risk_score }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="text-center py-5 text-muted">
|
||||
<div class="mb-3">NO_DATA_FOUND</div>
|
||||
<p class="small">INITIATE A SCAN TO BEGIN LOGGING</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% if recent_scans %}
|
||||
<div class="mt-4 text-center">
|
||||
<a href="#" class="btn btn-sm btn-outline-secondary w-100 text-uppercase letter-spacing-1 fw-bold py-2">LOAD_FULL_ARCHIVE</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
106
core/templates/core/scan_detail.html
Normal file
106
core/templates/core/scan_detail.html
Normal file
@ -0,0 +1,106 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row g-4 mb-5">
|
||||
<div class="col-lg-8">
|
||||
<div class="dashboard-card mb-4 border-0 p-0" style="background: transparent;">
|
||||
<a href="{% url 'index' %}" class="text-accent text-decoration-none small fw-bold mb-3 d-block">← BACK_TO_DASHBOARD</a>
|
||||
<h1 class="fw-bold mb-3 display-5 lh-1">REPORT_{{ scan.id }}_{{ scan.target_value|truncatechars:10 }}</h1>
|
||||
<div class="d-flex gap-4">
|
||||
<span class="text-muted fs-5 fw-medium">TARGET: {{ scan.target_value }}</span>
|
||||
<span class="text-accent fs-5 fw-bold">TYPE: {{ scan.target_type|upper }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 text-end">
|
||||
<div class="dashboard-card bg-dark border-accent d-inline-block text-center p-4">
|
||||
<h3 class="fs-4 fw-bold text-accent mb-0">RISK_SCORE</h3>
|
||||
<div class="display-3 fw-bold my-2 {% if scan.risk_score > 50 %}text-danger{% else %}text-success{% endif %}">
|
||||
{{ scan.risk_score }}
|
||||
</div>
|
||||
<div class="text-muted extra-small">CONFIDENCE: HIGH</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- Main Results Column -->
|
||||
<div class="col-lg-7">
|
||||
<h2 class="fs-5 mb-4 fw-bold">MODULE_OUTPUTS</h2>
|
||||
|
||||
{% for module, data in results.items %}
|
||||
{% if module != 'serp_results' %}
|
||||
<div class="dashboard-card mb-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h3 class="fs-6 fw-bold text-uppercase m-0">{{ module }}_DATA</h3>
|
||||
<span class="badge bg-dark border border-secondary text-accent small">LIVE_FEED</span>
|
||||
</div>
|
||||
<div class="module-content">
|
||||
<pre>{{ data|pprint }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<!-- Serp Results / Public Mentions -->
|
||||
{% if results.serp_results %}
|
||||
<div class="dashboard-card">
|
||||
<h3 class="fs-6 fw-bold text-uppercase mb-4 m-0 text-info">PUBLIC_MENTIONS_EXTRACT</h3>
|
||||
<div class="serp-list">
|
||||
{% for item in results.serp_results %}
|
||||
<div class="mb-4 pb-3 border-bottom border-secondary last-border-none">
|
||||
<a href="{{ item.link }}" target="_blank" class="text-info text-decoration-none fw-bold d-block mb-1 fs-6">
|
||||
{{ item.title }}
|
||||
</a>
|
||||
<p class="text-muted small mb-1">{{ item.snippet }}</p>
|
||||
<div class="text-accent extra-small fw-bold" style="font-size: 0.7rem;">SOURCE: {{ item.displayed_link }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Actions / Insights Column -->
|
||||
<div class="col-lg-5">
|
||||
<div class="dashboard-card mb-4">
|
||||
<h2 class="fs-6 fw-bold mb-4">INVESTIGATIVE_ACTIONS</h2>
|
||||
<div class="d-grid gap-3">
|
||||
<button class="btn btn-outline-info text-start py-3 fw-bold small letter-spacing-1">
|
||||
GENERATE_PDF_REPORT
|
||||
</button>
|
||||
<button class="btn btn-outline-warning text-start py-3 fw-bold small letter-spacing-1">
|
||||
FLAG_AS_SUSPICIOUS
|
||||
</button>
|
||||
<button class="btn btn-outline-danger text-start py-3 fw-bold small letter-spacing-1">
|
||||
EXPORT_FOR_MALTEGO
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary text-start py-3 fw-bold small letter-spacing-1">
|
||||
SHARE_SECURE_LINK
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-card">
|
||||
<h2 class="fs-6 fw-bold mb-4">FORENSIC_TIMELINE</h2>
|
||||
<div class="timeline ps-3 border-start border-secondary py-2">
|
||||
<div class="mb-4 position-relative">
|
||||
<div class="position-absolute bg-accent rounded-circle" style="width: 10px; height: 10px; left: -21px; top: 5px;"></div>
|
||||
<div class="small fw-bold">SCAN_INITIATED</div>
|
||||
<div class="text-muted extra-small">{{ scan.created_at|date:"Y-m-d H:i:s" }}</div>
|
||||
</div>
|
||||
<div class="mb-4 position-relative">
|
||||
<div class="position-absolute bg-info rounded-circle" style="width: 10px; height: 10px; left: -21px; top: 5px;"></div>
|
||||
<div class="small fw-bold">API_ENRICHMENT_COMPLETE</div>
|
||||
<div class="text-muted extra-small">SUCCESSFUL DATA FETCH</div>
|
||||
</div>
|
||||
<div class="position-relative">
|
||||
<div class="position-absolute bg-success rounded-circle" style="width: 10px; height: 10px; left: -21px; top: 5px;"></div>
|
||||
<div class="small fw-bold">REPORT_FINALIZED</div>
|
||||
<div class="text-muted extra-small">READY FOR REVIEW</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -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/<int:pk>/', ScanDetailView.as_view(), name='scan_detail'),
|
||||
]
|
||||
@ -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)
|
||||
@ -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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user