PrivHunterAI/index.html
2025-06-07 12:15:43 +08:00

815 lines
27 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PrivHunterAI</title>
<style>
body {
max-width: 1200px;
padding: 20px;
margin: 0 auto;
background: linear-gradient(135deg, #f8f9fa, #e9ecef);
font-family: 'Roboto', sans-serif;
}
/* 头部区域 - 增强的未来科技风格 */
.header {
position: relative;
border-radius: 15px;
padding: 35px;
color: white;
overflow: hidden;
background: linear-gradient(135deg, #1a6bd0, #4a90e2);
box-shadow: 0 10px 30px rgba(26, 107, 208, 0.3);
}
/* 网格背景效果 */
.grid-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
linear-gradient(to right, rgba(255,255,255,0.05) 1px, transparent 1px),
linear-gradient(to bottom, rgba(255,255,255,0.05) 1px, transparent 1px);
background-size: 30px 30px;
z-index: 0;
}
/* 六边形图案 */
.hexagon-pattern {
position: absolute;
width: 100%;
height: 100%;
z-index: 1;
overflow: hidden;
}
.hexagon {
position: absolute;
width: 60px;
height: 60px;
background: rgba(255,255,255,0.1);
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
animation: float 8s infinite ease-in-out;
}
/* 粒子效果 */
.particles {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 2;
}
.particle {
position: absolute;
width: 3px;
height: 3px;
background-color: rgba(255, 255, 255, 0.5);
border-radius: 50%;
box-shadow: 0 0 5px rgba(255, 255, 255, 0.8);
animation: particle-move 15s infinite linear;
}
/* 发光线条效果 */
.glow-lines {
position: absolute;
width: 100%;
height: 100%;
z-index: 1;
opacity: 0.2;
}
.glow-line {
position: absolute;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
animation: glow-move 8s infinite linear;
}
/* 标题样式 */
.header h1 {
font-size: 2.8rem;
font-weight: bold;
margin-bottom: 10px;
background: linear-gradient(to right, #ffffff, #a8d8ff);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-shadow: 0 0 15px rgba(255, 255, 255, 0.4);
position: relative;
display: inline-block;
z-index: 3;
animation: pulse 3s infinite ease-in-out;
}
@keyframes pulse {
0% {
text-shadow: 0 0 15px rgba(255, 255, 255, 0.4);
}
50% {
text-shadow: 0 0 25px rgba(255, 255, 255, 0.7), 0 0 35px rgba(255, 255, 255, 0.5);
}
100% {
text-shadow: 0 0 15px rgba(255, 255, 255, 0.4);
}
}
.header p {
font-size: 1.1rem;
opacity: 0.9;
margin-bottom: 25px;
z-index: 3;
position: relative;
}
.refresh-btn {
background: white;
color: #4a90e2;
border-radius: 5px;
padding: 10px 25px;
border: none;
font-size: 1rem;
cursor: pointer;
margin-top: 20px;
transition: all 0.3s ease;
z-index: 3;
position: relative;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
}
.refresh-btn:hover {
transform: translateY(-3px);
box-shadow: 0 6px 15px rgba(0,0,0,0.15);
}
/* 动画定义 */
@keyframes float {
0% {
transform: translate(-10px, -10px) rotate(0deg);
opacity: 0;
}
20% {
opacity: 0.8;
}
80% {
opacity: 0.8;
}
100% {
transform: translate(calc(100% + 10px), calc(100% + 10px)) rotate(360deg);
opacity: 0;
}
}
@keyframes particle-move {
0% {
transform: translate(0, 0) scale(0.5);
opacity: 0;
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
transform: translate(calc(var(--tx)), calc(var(--ty))) scale(0.5);
opacity: 0;
}
}
@keyframes glow-move {
0% {
transform: translateX(-100%) translateY(-100%);
}
100% {
transform: translateX(100%) translateY(100%);
}
}
/* 统计卡片样式 */
.stats-container {
display: flex;
gap: 20px;
margin-top: 30px;
}
.stat-card {
flex: 1;
background: white;
border-radius: 10px;
padding: 25px 20px;
text-align: center;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
transition: all 0.3s ease;
}
.stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 6px 12px rgba(0,0,0,0.08);
}
.stat-number {
font-size: 2.5rem;
font-weight: bold;
margin-bottom: 10px;
}
.total .stat-number { color: #4a90e2; }
.vulnerable .stat-number { color: #e74c3c; }
.unknown .stat-number { color: #f39c12; }
.safe .stat-number { color: #2ecc71; }
.stat-label {
font-size: 0.9rem;
color: #666;
}
/* 结果部分样式 */
.results-section {
background: white;
border-radius: 10px;
padding: 20px;
margin-top: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
}
.results-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.filter-select {
padding: 8px 15px;
border-radius: 5px;
border: 1px solid #ddd;
margin-right: 10px;
outline: none;
transition: all 0.3s ease;
}
.filter-select:focus {
border-color: #4a90e2;
box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
}
.filter-btn {
background: #4a90e2;
color: white;
border: none;
border-radius: 5px;
padding: 8px 20px;
cursor: pointer;
transition: all 0.3s ease;
}
.filter-btn:hover {
background: #3a7bc8;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(74, 144, 226, 0.3);
}
.export-btn {
background: #2ecc71;
color: white;
border: none;
border-radius: 5px;
padding: 8px 20px;
cursor: pointer;
margin-left: 10px;
transition: all 0.3s ease;
}
.export-btn:hover {
background: #27ae60;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(46, 204, 113, 0.3);
}
.empty-state {
text-align: center;
padding: 50px 20px;
color: #666;
}
footer {
text-align: center;
padding: 20px 0;
color: #666;
font-size: 0.9rem;
margin-top: 30px;
}
/* 表格样式 */
.data-table {
width: 100%;
border-collapse: collapse;
}
.data-table th {
padding: 1.5rem;
text-align: left;
vertical-align: middle;
max-width: 150px;
word-wrap: break-word;
border-bottom: 0.1rem solid #e0e0e0;
}
.data-table td {
padding: 1.5rem;
text-align: left;
vertical-align: middle;
max-width: 150px;
word-wrap: break-word;
border-bottom: 0.1rem solid #e0e0e0;
}
.data-table tr:hover {
background-color: rgba(0,0,0,0.02);
}
.foldable-row {
cursor: pointer;
}
.foldable-row .result-summary {
display: flex;
align-items: center;
}
.foldable-row .result-url {
text-align: left;
font-weight: bold;
word-wrap: break-word;
max-width: 500px;
margin-right: 15px;
flex-grow: 1;
font-size: 0.9rem;
}
.foldable-row .result-status {
padding: 4px 10px;
border-radius: 15px;
font-size: 0.8rem;
font-weight: bold;
}
.status-vulnerable {
/* margin-left: 100px; */
background-color: #ffebee;
color: #e53935;
}
.status-safe {
/* margin-left: 100px; */
background-color: #e8f5e9;
color: #388e3c;
}
.status-unknown {
/* margin-left: 100px; */
background-color: #fff8e1;
color: #ffa000;
}
.foldable-row .result-confidence {
margin-left: 20px;
font-size: 0.9rem;
color: #666;
}
.foldable-row .result-timestamp {
margin-right: 50px;
font-size: 0.9rem;
color: #666;
}
.details-row {
white-space: pre-line;
display: none;
padding-left: 20px;
}
.details-row table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
.details-row table td {
padding: 8px 0;
vertical-align: top;
}
.details-row table td:first-child {
font-weight: bold;
width: 120px;
}
.details-row.active {
display: table-row;
}
/* 分页样式 */
.pagination {
display: flex;
justify-content: center;
margin-top: 20px;
}
.pagination button {
background: #f8f9fa;
border: 1px solid #dee2e6;
padding: 8px 15px;
margin: 0 5px;
cursor: pointer;
border-radius: 5px;
transition: all 0.3s ease;
}
.pagination button:hover {
background: #e9ecef;
}
.pagination button.active {
background: #4a90e2;
color: white;
border-color: #4a90e2;
}
.pagination button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.page-info {
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
color: #666;
}
.page-info select {
margin: 0 10px;
padding: 5px 10px;
border-radius: 5px;
border: 1px solid #ddd;
}
</style>
</head>
<body>
<div class="container">
<!-- Header Section -->
<div class="header">
<!-- 网格背景 -->
<div class="grid-background"></div>
<!-- 六边形图案 -->
<div class="hexagon-pattern" id="hexagon-pattern"></div>
<!-- 发光线条 -->
<div class="glow-lines" id="glow-lines"></div>
<!-- 粒子效果 -->
<div class="particles" id="particles"></div>
<h1>PrivHunterAI <small>v1.0.0</small></h1>
<p>高级智能 API 安全扫描工具。</p>
<button class="refresh-btn" onclick="fetchData()">刷新数据</button>
</div>
<!-- Statistics Cards -->
<div class="stats-container">
<div class="stat-card total">
<div class="stat-number" id="total-requests">0</div>
<div class="stat-label">总扫描请求</div>
</div>
<div class="stat-card vulnerable">
<div class="stat-number" id="vulnerable-requests">0</div>
<div class="stat-label">漏洞请求</div>
</div>
<div class="stat-card unknown">
<div class="stat-number" id="unknown-requests">0</div>
<div class="stat-label">未知状态</div>
</div>
<div class="stat-card safe">
<div class="stat-number" id="safe-requests">0</div>
<div class="stat-label">安全请求</div>
</div>
</div>
<!-- Results Section -->
<div class="results-section">
<div class="results-header">
<h3>漏洞扫描结果</h3>
<div class="filters">
<select class="filter-select" id="filter-result">
<option value="">所有状态</option>
<option value="true">漏洞请求</option>
<option value="unknown">未知状态</option>
<option value="false">安全请求</option>
</select>
<button class="filter-btn" onclick="filterData()">筛选</button>
<button class="export-btn" onclick="exportToExcel()">导出Excel</button>
</div>
</div>
<table class="data-table">
<thead>
<tr>
<th style="width: 50%;">API 地址</th>
<th style="width: 25%;">状态信息</th>
<th style="width: 25%;">时间戳</th>
</tr>
</thead>
<tbody id="data-body">
<!-- 表格数据将通过 JavaScript 动态添加 -->
</tbody>
</table>
<!-- 分页控制 -->
<div class="pagination" id="pagination">
<select id="page-size">
<option value="5">5 条/页</option>
<option value="10" selected>10 条/页</option>
<option value="20">20 条/页</option>
<option value="50">50 条/页</option>
<option value="100">100 条/页</option>
</select>
<button onclick="prevPage()" id="prev-btn" disabled>上一页</button>
<span id="page-info">第 1 页,共 1 页</span>
<button onclick="nextPage()" id="next-btn" disabled>下一页</button>
</div>
</div>
</div>
<footer>
© 2025 PrivHunterAI专注智能安全检测领航 API 安全未来。
</footer>
<script>
// 分页相关变量
let currentPage = 1;
let itemsPerPage = 10;
let totalPages = 1;
let currentData = [];
async function fetchData(page = 1, pageSize = itemsPerPage, resultFilter = '') {
try {
document.body.style.cursor = 'wait';
// 获取统计数据
const statsResponse = await fetch('/stats');
if (!statsResponse.ok) throw new Error('Failed to fetch statistics');
const stats = await statsResponse.json();
updateStatistics(stats);
// 获取分页数据
const dataResponse = await fetch(`/data?page=${page}&pageSize=${pageSize}&result=${resultFilter}`);
if (!dataResponse.ok) throw new Error('Failed to fetch data');
const data = await dataResponse.json();
currentData = data.data;
currentPage = data.currentPage;
itemsPerPage = data.pageSize;
totalPages = data.totalPages;
updateTableData();
updatePaginationControls();
} finally {
document.body.style.cursor = 'default';
}
}
function updateStatistics(stats) {
document.getElementById('total-requests').textContent = stats.total;
document.getElementById('vulnerable-requests').textContent = stats.vulnerable;
document.getElementById('unknown-requests').textContent = stats.unknown;
document.getElementById('safe-requests').textContent = stats.safe;
}
function updateTableData() {
const tableBody = document.getElementById('data-body');
tableBody.innerHTML = '';
if (currentData.length === 0) {
const emptyRow = document.createElement('tr');
emptyRow.innerHTML = `
<td colspan="3" class="empty-state">
<i class="fas fa-search"></i>
<p>没有找到符合条件的数据</p>
</td>
`;
tableBody.appendChild(emptyRow);
return;
}
currentData.forEach(item => {
const foldableRow = document.createElement('tr');
foldableRow.className = 'foldable-row';
foldableRow.onclick = function() {
const detailsRow = this.nextElementSibling;
detailsRow.classList.toggle('active');
this.style.fontWeight = detailsRow.classList.contains('active') ? 'bold' : 'normal';
};
if (item.result === 'true') {
foldableRow.style.borderLeft = '4px solid #e74c3c';
} else if (item.result === 'unknown') {
foldableRow.style.borderLeft = '4px solid #f39c12';
} else if (item.result === 'false') {
foldableRow.style.borderLeft = '4px solid #2ecc71';
}
foldableRow.innerHTML = `
<td>
<div class="result-summary">
<span class="result-url">${item.url.split('?')[0]}</span>
</div>
</td>
<td>
<div class="result-summary">
<span class="result-status status-${item.result === 'true' ? 'vulnerable' : item.result === 'unknown' ? 'unknown' : 'safe'}">
${item.result === 'true' ? '漏洞' : item.result === 'unknown' ? '未知' : '安全'}
</span>
<span class="result-confidence">${item.confidence}</span>
</div>
</td>
<td>
<div class="result-summary">
<span class="result-timestamp">${item.timestamp}</span>
<span>&#9662;</span>
</div>
</td>
`;
tableBody.appendChild(foldableRow);
const detailsRow = document.createElement('tr');
detailsRow.className = 'details-row';
detailsRow.innerHTML = `
<td colspan="3">
<table>
<tr>
<td>Method:</td>
<td>${item.method || ''}</td>
</tr>
<tr>
<td>URL:</td>
<td>${item.url || ''}</td>
</tr>
<tr>
<td>RequestA:</td>
<td>${item.requestA || ''}</td>
</tr>
<tr>
<td>RespBodyA:</td>
<td>${item.respBodyA || ''}</td>
</tr>
<tr>
<td>RequestB:</td>
<td>${item.requestB || ''}</td>
</tr>
<tr>
<td>RespBodyB:</td>
<td>${item.respBodyB || ''}</td>
</tr>
<tr>
<td>Result:</td>
<td>${item.result === 'unknown' ? '未知' : item.result === 'false' ? '安全' : '漏洞'}</td>
</tr>
<tr>
<td>Reason:</td>
<td>${item.reason || ''}</td>
</tr>
<tr>
<td>Confidence:</td>
<td>${item.confidence || ''}</td>
</tr>
</table>
</td>
`;
tableBody.appendChild(detailsRow);
});
}
function updatePaginationControls() {
document.getElementById('page-info').textContent =
`${currentPage} 页,共 ${totalPages}`;
document.getElementById('prev-btn').disabled = currentPage === 1;
document.getElementById('next-btn').disabled = currentPage === totalPages;
}
function prevPage() {
if (currentPage > 1) {
fetchData(currentPage - 1, itemsPerPage, document.getElementById('filter-result').value);
}
}
function nextPage() {
if (currentPage < totalPages) {
fetchData(currentPage + 1, itemsPerPage, document.getElementById('filter-result').value);
}
}
function filterData() {
const resultFilter = document.getElementById('filter-result').value;
fetchData(1, itemsPerPage, resultFilter);
}
document.getElementById('page-size').addEventListener('change', function() {
itemsPerPage = parseInt(this.value);
fetchData(1, itemsPerPage, document.getElementById('filter-result').value);
});
// 导出Excel功能
async function exportToExcel() {
try {
document.body.style.cursor = 'wait';
// 获取当前筛选条件
const resultFilter = document.getElementById('filter-result').value;
// 调用后端导出接口
const response = await fetch(`/export?result=${resultFilter}`);
if (!response.ok) {
throw new Error('导出失败');
}
// 将响应转换为blob
const blob = await response.blob();
// 创建一个临时下载链接
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
// 设置文件名
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const filename = `PrivHunterAI-扫描结果-${timestamp}.xlsx`;
a.download = filename;
// 触发下载
document.body.appendChild(a);
a.click();
// 清理
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
} catch (error) {
alert('导出失败: ' + error.message);
} finally {
document.body.style.cursor = 'default';
}
}
document.addEventListener('DOMContentLoaded', () => {
fetchData();
// 创建粒子效果
const particlesContainer = document.getElementById('particles');
for (let i = 0; i < 30; i++) {
const particle = document.createElement('div');
particle.classList.add('particle');
particle.style.left = `${Math.random() * 100}%`;
particle.style.top = `${Math.random() * 100}%`;
// 随机设置粒子运动轨迹
const tx = (Math.random() - 0.5) * 200;
const ty = (Math.random() - 0.5) * 200;
particle.style.setProperty('--tx', `${tx}px`);
particle.style.setProperty('--ty', `${ty}px`);
// 随机动画延迟
particle.style.animationDelay = `${Math.random() * 15}s`;
particle.style.animationDuration = `${15 + Math.random() * 10}s`;
particlesContainer.appendChild(particle);
}
// 创建六边形图案
const hexagonPattern = document.getElementById('hexagon-pattern');
for (let i = 0; i < 15; i++) {
const hexagon = document.createElement('div');
hexagon.classList.add('hexagon');
hexagon.style.left = `${Math.random() * 100}%`;
hexagon.style.top = `${Math.random() * 100}%`;
hexagon.style.animationDelay = `${Math.random() * 5}s`;
hexagonPattern.appendChild(hexagon);
}
// 创建发光线条
const glowLinesContainer = document.getElementById('glow-lines');
for (let i = 0; i < 8; i++) {
const line = document.createElement('div');
line.classList.add('glow-line');
line.style.width = `${30 + Math.random() * 70}%`;
line.style.left = `${Math.random() * 70}%`;
line.style.top = `${Math.random() * 70}%`;
line.style.transform = `rotate(${Math.random() * 360}deg)`;
line.style.animationDelay = `${Math.random() * 5}s`;
glowLinesContainer.appendChild(line);
}
});
</script>
</body>
</html>