This commit is contained in:
2026-04-20 14:22:58 +09:00
parent 38c802950a
commit d9abe6f67b
4 changed files with 64 additions and 0 deletions
+25
View File
@@ -56,6 +56,7 @@
<button type="button" class="tls-tab" data-tab="vulnerabilities" role="tab" aria-selected="false">脆弱性</button> <button type="button" class="tls-tab" data-tab="vulnerabilities" role="tab" aria-selected="false">脆弱性</button>
<button type="button" class="tls-tab" data-tab="compatibility" role="tab" aria-selected="false">互換性</button> <button type="button" class="tls-tab" data-tab="compatibility" role="tab" aria-selected="false">互換性</button>
<button type="button" class="tls-tab" data-tab="log" role="tab" aria-selected="false">ログ</button> <button type="button" class="tls-tab" data-tab="log" role="tab" aria-selected="false">ログ</button>
<button type="button" class="tls-tab" data-tab="history" role="tab" aria-selected="false">テスト履歴</button>
<button type="button" class="tls-tab" data-tab="json" role="tab" aria-selected="false">JSON</button> <button type="button" class="tls-tab" data-tab="json" role="tab" aria-selected="false">JSON</button>
</nav> </nav>
</header> </header>
@@ -399,6 +400,30 @@
</section> </section>
</div> </div>
{# -------- テスト履歴 -------- #}
<div class="tls-tab-panel" data-panel="history">
<section class="tls-section">
<h2 class="tls-section-title">過去のテスト結果 (同一対象)</h2>
{% if history %}
<table class="tls-table">
<thead><tr><th>実行日時</th><th>ランク</th><th>スコア</th><th>リンク</th></tr></thead>
<tbody>
{% for h in history %}
<tr>
<td>{{ h.finished_at_display or '—' }}</td>
<td>{{ h.rank or '—' }}</td>
<td>{{ "%.0f"|format(h.score or 0) }}</td>
<td><a href="/tools/tls-test/results/{{ h.id }}/" class="text-bright-green">{{ h.id[:8] }}…</a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="tls-log-row"><span class="tls-log-cat text-tx-alt">[info]</span><span class="tls-log-msg text-tx-alt">過去のテスト結果はありません</span></div>
{% endif %}
</section>
</div>
{# -------- JSON -------- #} {# -------- JSON -------- #}
<div class="tls-tab-panel" data-panel="json"> <div class="tls-tab-panel" data-panel="json">
<section class="tls-section"> <section class="tls-section">
+25
View File
@@ -58,6 +58,7 @@ class TlsTestDB:
); );
CREATE INDEX IF NOT EXISTS idx_tests_expires ON tests(expires_at); CREATE INDEX IF NOT EXISTS idx_tests_expires ON tests(expires_at);
CREATE INDEX IF NOT EXISTS idx_tests_ip_created ON tests(client_ip, created_at); CREATE INDEX IF NOT EXISTS idx_tests_ip_created ON tests(client_ip, created_at);
CREATE INDEX IF NOT EXISTS idx_tests_target ON tests(target, created_at);
CREATE TABLE IF NOT EXISTS test_progress ( CREATE TABLE IF NOT EXISTS test_progress (
test_id TEXT NOT NULL, test_id TEXT NOT NULL,
@@ -185,6 +186,30 @@ class TlsTestDB:
finally: finally:
conn.close() conn.close()
def get_history_by_target(self, target: str, exclude_id: str | None = None, limit: int = 10) -> list[dict[str, Any]]:
conn = self._conn()
try:
cur = conn.cursor()
if exclude_id:
cur.execute(
"SELECT id, target, status, created_at, finished_at, rank, score, error_message"
" FROM tests WHERE target = ? AND id != ? AND status = 'done'"
" ORDER BY created_at DESC LIMIT ?",
(target, exclude_id, limit),
)
else:
cur.execute(
"SELECT id, target, status, created_at, finished_at, rank, score, error_message"
" FROM tests WHERE target = ? AND status = 'done'"
" ORDER BY created_at DESC LIMIT ?",
(target, limit),
)
rows = cur.fetchall()
cols = [c[0] for c in cur.description]
return [dict(zip(cols, r)) for r in rows]
finally:
conn.close()
def count_ip_in_window(self, client_ip: str, window_seconds: int) -> int: def count_ip_in_window(self, client_ip: str, window_seconds: int) -> int:
cutoff = int(time.time()) - window_seconds cutoff = int(time.time()) - window_seconds
conn = self._conn() conn = self._conn()
@@ -159,8 +159,12 @@ async def _gather(report: ReportProgress, finds: ReportFinding, result: ScanResu
# SSL 2/3 → vulnerability findings; also record them as safety findings. # SSL 2/3 → vulnerability findings; also record them as safety findings.
if sslv2_supported: if sslv2_supported:
await emit(Finding("SSL/TLS Version", "SSL 2.0 supported", "SSLv2 は完全に破綻しています (DROWN)", "serious", 10, group=G_SAF)) await emit(Finding("SSL/TLS Version", "SSL 2.0 supported", "SSLv2 は完全に破綻しています (DROWN)", "serious", 10, group=G_SAF))
else:
await emit(Finding("SSL/TLS Version", "SSL 2.0 disabled", "", "good", 0, group=G_SAF))
if sslv3_supported: if sslv3_supported:
await emit(Finding("SSL/TLS Version", "SSL 3.0 supported", "POODLE 攻撃が可能", "serious", 10, group=G_SAF)) await emit(Finding("SSL/TLS Version", "SSL 3.0 supported", "POODLE 攻撃が可能", "serious", 10, group=G_SAF))
else:
await emit(Finding("SSL/TLS Version", "SSL 3.0 disabled", "", "good", 0, group=G_SAF))
if version_support.get(C.TLS_1_0): if version_support.get(C.TLS_1_0):
await emit(Finding("SSL/TLS Version", "TLS 1.0 supported", "RFC 8996 で廃止済み", "notgood", 4, group=G_SAF)) await emit(Finding("SSL/TLS Version", "TLS 1.0 supported", "RFC 8996 で廃止済み", "notgood", 4, group=G_SAF))
else: else:
@@ -80,6 +80,15 @@ def tls_results_context(job: dict, test_id: str, request: Request, tls_test_db)
if step not in seen_steps: if step not in seen_steps:
log_entries += [{"kind": "finding", "finding": f} for f in fs] log_entries += [{"kind": "finding", "finding": f} for f in fs]
target = (result.get("target") or job.get("target") or "").strip()
history = tls_test_db.get_history_by_target(target, exclude_id=test_id, limit=10) if target else []
for h in history:
try:
ts = h.get("finished_at") or h.get("created_at") or 0
h["finished_at_display"] = datetime.datetime.fromtimestamp(int(ts)).strftime("%Y-%m-%dT%H:%M:%S") if ts else ""
except Exception:
h["finished_at_display"] = ""
return { return {
"test_id": test_id, "test_id": test_id,
"job": job, "job": job,
@@ -90,4 +99,5 @@ def tls_results_context(job: dict, test_id: str, request: Request, tls_test_db)
"finished_at_display": finished_at_display, "finished_at_display": finished_at_display,
"share_url": str(request.url).split("#", 1)[0], "share_url": str(request.url).split("#", 1)[0],
"log_entries": log_entries, "log_entries": log_entries,
"history": history,
} }