--
This commit is contained in:
@@ -30,17 +30,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- JSON-copy button (inside the JSON tab) ----
|
// ---- Shared copy helper (clipboard API + textarea fallback) ----
|
||||||
const copyJsonBtn = document.getElementById("tls-copy-json");
|
async function copyText(text) {
|
||||||
const rawJsonEl = document.getElementById("tls-raw-json");
|
|
||||||
if (copyJsonBtn && rawJsonEl) {
|
|
||||||
const origLabel = copyJsonBtn.textContent;
|
|
||||||
copyJsonBtn.addEventListener("click", async () => {
|
|
||||||
const text = rawJsonEl.textContent || "";
|
|
||||||
try {
|
try {
|
||||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
await navigator.clipboard.writeText(text);
|
await navigator.clipboard.writeText(text);
|
||||||
} else {
|
return true;
|
||||||
|
}
|
||||||
const ta = document.createElement("textarea");
|
const ta = document.createElement("textarea");
|
||||||
ta.value = text;
|
ta.value = text;
|
||||||
ta.style.position = "fixed";
|
ta.style.position = "fixed";
|
||||||
@@ -49,20 +45,35 @@
|
|||||||
ta.select();
|
ta.select();
|
||||||
try { document.execCommand("copy"); } catch (_) {}
|
try { document.execCommand("copy"); } catch (_) {}
|
||||||
document.body.removeChild(ta);
|
document.body.removeChild(ta);
|
||||||
}
|
return true;
|
||||||
copyJsonBtn.textContent = "コピーしました";
|
|
||||||
copyJsonBtn.classList.add("is-done");
|
|
||||||
setTimeout(() => {
|
|
||||||
copyJsonBtn.textContent = origLabel;
|
|
||||||
copyJsonBtn.classList.remove("is-done");
|
|
||||||
}, 1500);
|
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
copyJsonBtn.textContent = "コピー失敗";
|
return false;
|
||||||
setTimeout(() => { copyJsonBtn.textContent = origLabel; }, 1500);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap a button's label and is-done class for 1.5s based on action result.
|
||||||
|
function bindCopyButton(btn, getText) {
|
||||||
|
if (!btn) return;
|
||||||
|
const orig = btn.textContent;
|
||||||
|
btn.addEventListener("click", async () => {
|
||||||
|
const ok = await copyText(getText());
|
||||||
|
btn.textContent = ok ? "コピーしました" : "コピー失敗";
|
||||||
|
if (ok) btn.classList.add("is-done");
|
||||||
|
setTimeout(() => {
|
||||||
|
btn.textContent = orig;
|
||||||
|
btn.classList.remove("is-done");
|
||||||
|
}, 1500);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- JSON-copy button (inside the JSON tab) ----
|
||||||
|
const rawJsonEl = document.getElementById("tls-raw-json");
|
||||||
|
bindCopyButton(document.getElementById("tls-copy-json"), () => (rawJsonEl && rawJsonEl.textContent) || "");
|
||||||
|
|
||||||
|
// ---- Copy-link button ----
|
||||||
|
const copyLinkBtn = document.getElementById("tls-copy-link");
|
||||||
|
bindCopyButton(copyLinkBtn, () => (copyLinkBtn && copyLinkBtn.dataset.link) || location.href);
|
||||||
|
|
||||||
// ---- PDF / print button ----
|
// ---- PDF / print button ----
|
||||||
const printBtn = document.getElementById("tls-print-pdf");
|
const printBtn = document.getElementById("tls-print-pdf");
|
||||||
if (printBtn) {
|
if (printBtn) {
|
||||||
@@ -88,37 +99,4 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Copy-link button ----
|
|
||||||
const copyBtn = document.getElementById("tls-copy-link");
|
|
||||||
if (copyBtn) {
|
|
||||||
const origLabel = copyBtn.textContent;
|
|
||||||
copyBtn.addEventListener("click", async () => {
|
|
||||||
const link = copyBtn.dataset.link || location.href;
|
|
||||||
try {
|
|
||||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
||||||
await navigator.clipboard.writeText(link);
|
|
||||||
} else {
|
|
||||||
// Fallback: use a hidden textarea + document.execCommand
|
|
||||||
const ta = document.createElement("textarea");
|
|
||||||
ta.value = link;
|
|
||||||
ta.style.position = "fixed";
|
|
||||||
ta.style.opacity = "0";
|
|
||||||
document.body.appendChild(ta);
|
|
||||||
ta.select();
|
|
||||||
try { document.execCommand("copy"); } catch (_) {}
|
|
||||||
document.body.removeChild(ta);
|
|
||||||
}
|
|
||||||
copyBtn.textContent = "コピーしました";
|
|
||||||
copyBtn.classList.add("is-done");
|
|
||||||
setTimeout(() => {
|
|
||||||
copyBtn.textContent = origLabel;
|
|
||||||
copyBtn.classList.remove("is-done");
|
|
||||||
}, 1500);
|
|
||||||
} catch (_) {
|
|
||||||
copyBtn.textContent = "コピー失敗";
|
|
||||||
setTimeout(() => { copyBtn.textContent = origLabel; }, 1500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
/* =====================================================
|
/* Nercone TLS Test — shared styles. Palette uses CSS vars from /assets/css/style.css. */
|
||||||
* Nercone TLS Test — shared styles
|
|
||||||
* Palette comes from /public/assets/css/style.css
|
|
||||||
* #1A1A1A = page bg #272727 = block bg
|
|
||||||
* #202020 = input bg #3a3a3a = border
|
|
||||||
* #E0E0E0 = tx #939393 = light-grey-alt
|
|
||||||
* #00C878 = bright-green #00C0FF = bright-blue
|
|
||||||
* ===================================================== */
|
|
||||||
|
|
||||||
/* ------ Landing page ------ */
|
/* ------ Landing page ------ */
|
||||||
.tls-landing {
|
.tls-landing {
|
||||||
@@ -22,7 +15,6 @@
|
|||||||
margin: 0 0 8px 0;
|
margin: 0 0 8px 0;
|
||||||
font-size: clamp(40pt, 6vw, 64pt);
|
font-size: clamp(40pt, 6vw, 64pt);
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
color: #FFFFFF;
|
|
||||||
letter-spacing: -0.01em;
|
letter-spacing: -0.01em;
|
||||||
}
|
}
|
||||||
.tls-landing-subtitle {
|
.tls-landing-subtitle {
|
||||||
@@ -40,21 +32,21 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
background-color: #3a3a3a;
|
background-color: #3a3a3a;
|
||||||
color: #E0E0E0;
|
color: var(--color-light-grey);
|
||||||
border: 1px solid #4a4a4a;
|
border: 1px solid #4a4a4a;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-size: 13pt;
|
font-size: 13pt;
|
||||||
}
|
}
|
||||||
.tls-landing-input::placeholder { color: #939393; }
|
.tls-landing-input::placeholder { color: var(--color-light-grey-alt); }
|
||||||
.tls-landing-input:focus {
|
.tls-landing-input:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: #00C0FF;
|
border-color: var(--color-bright-blue);
|
||||||
}
|
}
|
||||||
.tls-landing-submit {
|
.tls-landing-submit {
|
||||||
background-color: #0096D0;
|
background-color: #0096D0;
|
||||||
color: #FFFFFF;
|
color: var(--color-white);
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 12px 28px;
|
padding: 12px 28px;
|
||||||
@@ -75,10 +67,10 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
.tls-landing-links a {
|
.tls-landing-links a {
|
||||||
color: #939393;
|
color: var(--color-light-grey-alt);
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
.tls-landing-links a:hover { color: #E0E0E0; }
|
.tls-landing-links a:hover { color: var(--color-light-grey); }
|
||||||
|
|
||||||
.tls-aux-section {
|
.tls-aux-section {
|
||||||
max-width: 900px;
|
max-width: 900px;
|
||||||
@@ -110,9 +102,6 @@
|
|||||||
.tls-status-testid {
|
.tls-status-testid {
|
||||||
margin: 10px 0 0 0;
|
margin: 10px 0 0 0;
|
||||||
}
|
}
|
||||||
.tls-status-testid code {
|
|
||||||
font-family: "MesloLGS Nerd Font", "MesloLGS NF", "Menlo", "Consolas", monospace;
|
|
||||||
}
|
|
||||||
.tls-progress-track {
|
.tls-progress-track {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 6px;
|
height: 6px;
|
||||||
@@ -122,7 +111,7 @@
|
|||||||
}
|
}
|
||||||
.tls-progress-bar {
|
.tls-progress-bar {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: #00C878;
|
background-color: var(--color-bright-green);
|
||||||
transition: width 0.25s ease;
|
transition: width 0.25s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +119,7 @@
|
|||||||
.tls-log-wrap {
|
.tls-log-wrap {
|
||||||
width: min(1100px, 100%);
|
width: min(1100px, 100%);
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
background-color: #272727;
|
background-color: var(--color-dark-grey-alt);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 18px 22px;
|
padding: 18px 22px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@@ -183,10 +172,10 @@
|
|||||||
.tls-results-header {
|
.tls-results-header {
|
||||||
position: static;
|
position: static;
|
||||||
display: block;
|
display: block;
|
||||||
background-color: #272727;
|
background-color: var(--color-dark-grey-alt);
|
||||||
padding: 28px clamp(16px, 4vw, 40px) 0;
|
padding: 28px clamp(16px, 4vw, 40px) 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
border-bottom: 1px solid #1a1a1a;
|
border-bottom: 1px solid var(--color-dark-grey);
|
||||||
backdrop-filter: none;
|
backdrop-filter: none;
|
||||||
}
|
}
|
||||||
.tls-results-header::before {
|
.tls-results-header::before {
|
||||||
@@ -234,7 +223,6 @@
|
|||||||
margin: 0 0 4px 0;
|
margin: 0 0 4px 0;
|
||||||
font-size: 22pt;
|
font-size: 22pt;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #FFFFFF;
|
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
.tls-results-metaline {
|
.tls-results-metaline {
|
||||||
@@ -249,7 +237,6 @@
|
|||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
}
|
}
|
||||||
.tls-results-testid code {
|
.tls-results-testid code {
|
||||||
font-family: "MesloLGS Nerd Font", "MesloLGS NF", "Menlo", "Consolas", monospace;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
.tls-results-actions {
|
.tls-results-actions {
|
||||||
@@ -271,15 +258,15 @@
|
|||||||
}
|
}
|
||||||
.tls-btn-secondary {
|
.tls-btn-secondary {
|
||||||
background-color: #3a3a3a;
|
background-color: #3a3a3a;
|
||||||
color: #E0E0E0;
|
color: var(--color-light-grey);
|
||||||
}
|
}
|
||||||
.tls-btn-secondary:hover { background-color: #4a4a4a; }
|
.tls-btn-secondary:hover { background-color: #4a4a4a; }
|
||||||
.tls-btn-primary {
|
.tls-btn-primary {
|
||||||
background-color: #0096D0;
|
background-color: #0096D0;
|
||||||
color: #FFFFFF;
|
color: var(--color-white);
|
||||||
}
|
}
|
||||||
.tls-btn-primary:hover { background-color: #00B4F0; }
|
.tls-btn-primary:hover { background-color: #00B4F0; }
|
||||||
.tls-btn-primary.is-done { background-color: #00A050; }
|
.tls-btn-primary.is-done { background-color: var(--color-green); }
|
||||||
|
|
||||||
/* Tabs — sit at the bottom of the results header banner. */
|
/* Tabs — sit at the bottom of the results header banner. */
|
||||||
.tls-results-header .tls-tabs {
|
.tls-results-header .tls-tabs {
|
||||||
@@ -291,11 +278,11 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 0;
|
gap: 0;
|
||||||
margin: 12px 0 16px 0;
|
margin: 12px 0 16px 0;
|
||||||
border-bottom: 1px solid #1a1a1a;
|
border-bottom: 1px solid var(--color-dark-grey);
|
||||||
}
|
}
|
||||||
.tls-tab {
|
.tls-tab {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: #939393;
|
color: var(--color-light-grey-alt);
|
||||||
border: none;
|
border: none;
|
||||||
border-bottom: 2px solid transparent;
|
border-bottom: 2px solid transparent;
|
||||||
padding: 10px 18px 10px 18px;
|
padding: 10px 18px 10px 18px;
|
||||||
@@ -305,12 +292,12 @@
|
|||||||
margin-bottom: -1px;
|
margin-bottom: -1px;
|
||||||
}
|
}
|
||||||
.tls-tab:hover {
|
.tls-tab:hover {
|
||||||
color: #E0E0E0;
|
color: var(--color-light-grey);
|
||||||
}
|
}
|
||||||
.tls-tab.is-active {
|
.tls-tab.is-active {
|
||||||
color: #FFFFFF;
|
color: var(--color-white);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
border-bottom-color: #E0E0E0;
|
border-bottom-color: var(--color-light-grey);
|
||||||
}
|
}
|
||||||
.tls-tab-panels {
|
.tls-tab-panels {
|
||||||
padding: 0 clamp(16px, 4vw, 40px) 40px;
|
padding: 0 clamp(16px, 4vw, 40px) 40px;
|
||||||
@@ -332,7 +319,6 @@
|
|||||||
.tls-section-title {
|
.tls-section-title {
|
||||||
font-size: 14pt;
|
font-size: 14pt;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #FFFFFF;
|
|
||||||
margin: 0 0 10px 0;
|
margin: 0 0 10px 0;
|
||||||
padding-bottom: 6px;
|
padding-bottom: 6px;
|
||||||
border-bottom: 1px solid #3a3a3a;
|
border-bottom: 1px solid #3a3a3a;
|
||||||
@@ -346,7 +332,7 @@
|
|||||||
background-color: #202020;
|
background-color: #202020;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
color: #E0E0E0;
|
color: var(--color-light-grey);
|
||||||
}
|
}
|
||||||
.tls-table th,
|
.tls-table th,
|
||||||
.tls-table td {
|
.tls-table td {
|
||||||
@@ -357,7 +343,7 @@
|
|||||||
}
|
}
|
||||||
.tls-table thead th {
|
.tls-table thead th {
|
||||||
background-color: #2a2a2a;
|
background-color: #2a2a2a;
|
||||||
color: #939393;
|
color: var(--color-light-grey-alt);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 10.5pt;
|
font-size: 10.5pt;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
@@ -368,7 +354,6 @@
|
|||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
.tls-table code {
|
.tls-table code {
|
||||||
font-family: "MesloLGS Nerd Font", "MesloLGS NF", "Menlo", "Consolas", monospace;
|
|
||||||
font-size: 10.5pt;
|
font-size: 10.5pt;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -381,7 +366,7 @@
|
|||||||
.tls-cipher-version {
|
.tls-cipher-version {
|
||||||
margin: 0 0 6px 0;
|
margin: 0 0 6px 0;
|
||||||
font-size: 11.5pt;
|
font-size: 11.5pt;
|
||||||
color: #E0E0E0;
|
color: var(--color-light-grey);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
.tls-cipher-list {
|
.tls-cipher-list {
|
||||||
@@ -395,9 +380,8 @@
|
|||||||
gap: 4px 16px;
|
gap: 4px 16px;
|
||||||
}
|
}
|
||||||
.tls-cipher-list code {
|
.tls-cipher-list code {
|
||||||
font-family: "MesloLGS Nerd Font", "MesloLGS NF", "Menlo", "Consolas", monospace;
|
|
||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
color: #E0E0E0;
|
color: var(--color-light-grey);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
@@ -421,36 +405,28 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
.tls-raw code {
|
.tls-raw code {
|
||||||
font-family: "MesloLGS Nerd Font", "MesloLGS NF", "Menlo", "Consolas", monospace;
|
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------
|
/* Print stylesheet — "PDFをダウンロード" uses window.print(). Hide chrome and
|
||||||
* Print stylesheet — "PDFをダウンロード" uses window.print() which
|
* expand all tab panels so the resulting PDF contains every finding. */
|
||||||
* lets the user save the page as a PDF from the browser print dialog.
|
|
||||||
* Hide chrome (tabs, buttons, site header/footer) and expand all tab
|
|
||||||
* panels so the resulting PDF contains every finding.
|
|
||||||
* ------------------------------------------------------------------ */
|
|
||||||
@media print {
|
@media print {
|
||||||
html, body {
|
html, body {
|
||||||
background: #ffffff !important;
|
background: #ffffff !important;
|
||||||
color: #101010 !important;
|
color: #101010 !important;
|
||||||
}
|
}
|
||||||
/* Hide site-wide chrome and interactive controls. */
|
|
||||||
footer, .tls-tabs, .tls-results-actions,
|
footer, .tls-tabs, .tls-results-actions,
|
||||||
#tls-copy-json, script, .tls-landing-links {
|
#tls-copy-json, script, .tls-landing-links {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
/* Keep the results-header banner but make it paper-friendly. */
|
|
||||||
.tls-results-header {
|
.tls-results-header {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
border: 0 !important;
|
border: 0 !important;
|
||||||
padding: 0 0 16px 0 !important;
|
padding: 0 0 16px 0 !important;
|
||||||
border-bottom: 1px solid #cccccc !important;
|
border-bottom: 1px solid #cccccc !important;
|
||||||
}
|
}
|
||||||
/* Expand every tab panel so the full report prints. */
|
|
||||||
.tls-tab-panels {
|
.tls-tab-panels {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
@@ -481,7 +457,6 @@
|
|||||||
border-bottom: 1px dotted #cccccc;
|
border-bottom: 1px dotted #cccccc;
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
}
|
}
|
||||||
/* Rank badge keeps its colour via currentColor. */
|
|
||||||
.tls-rank-badge {
|
.tls-rank-badge {
|
||||||
print-color-adjust: exact;
|
print-color-adjust: exact;
|
||||||
-webkit-print-color-adjust: exact;
|
-webkit-print-color-adjust: exact;
|
||||||
|
|||||||
@@ -7,24 +7,15 @@
|
|||||||
const logEl = document.getElementById("tls-log");
|
const logEl = document.getElementById("tls-log");
|
||||||
const verbEl = document.getElementById("tls-status-verb");
|
const verbEl = document.getElementById("tls-status-verb");
|
||||||
|
|
||||||
// Colors for the severity label tag. Order Good→Bad maps to
|
// Severity label tag. Matches SEVERITY_COLORS in schemas.py so the live
|
||||||
// bright-{green,blue,yellow,red,purple}. Matches SEVERITY_COLORS in
|
// log and results page render identically.
|
||||||
// schemas.py so the live log and results page render identically.
|
const SEV = {
|
||||||
const SEV_COLOR = {
|
good: { color: "bright-green", label: "GOOD" },
|
||||||
good: "bright-green",
|
normal: { color: "bright-blue", label: "NORMAL" },
|
||||||
normal: "bright-blue",
|
notgood: { color: "bright-yellow", label: "NOT GOOD" },
|
||||||
notgood: "bright-yellow",
|
bad: { color: "bright-red", label: "BAD" },
|
||||||
bad: "bright-red",
|
serious: { color: "bright-purple", label: "SERIOUS" },
|
||||||
serious: "bright-purple",
|
info: { color: "light-grey-alt", label: "INFO" },
|
||||||
info: "light-grey-alt",
|
|
||||||
};
|
|
||||||
const SEV_LABEL = {
|
|
||||||
good: "GOOD",
|
|
||||||
normal: "NORMAL",
|
|
||||||
notgood: "NOT GOOD",
|
|
||||||
bad: "BAD",
|
|
||||||
serious: "SERIOUS",
|
|
||||||
info: "INFO",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let reconnectAttempts = 0;
|
let reconnectAttempts = 0;
|
||||||
@@ -50,10 +41,10 @@
|
|||||||
|
|
||||||
const msg = document.createElement("span");
|
const msg = document.createElement("span");
|
||||||
msg.className = "tls-log-msg";
|
msg.className = "tls-log-msg";
|
||||||
if (severity && SEV_LABEL[severity] && severity !== "info") {
|
if (severity && SEV[severity] && severity !== "info") {
|
||||||
const sev = document.createElement("span");
|
const sev = document.createElement("span");
|
||||||
sev.className = "text-" + (SEV_COLOR[severity] || "light-grey") + " font-bold";
|
sev.className = "text-" + SEV[severity].color + " font-bold";
|
||||||
sev.textContent = SEV_LABEL[severity];
|
sev.textContent = SEV[severity].label;
|
||||||
msg.appendChild(sev);
|
msg.appendChild(sev);
|
||||||
msg.appendChild(document.createTextNode(" "));
|
msg.appendChild(document.createTextNode(" "));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,18 +11,17 @@ from bs4 import BeautifulSoup
|
|||||||
from markitdown import MarkItDown
|
from markitdown import MarkItDown
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from fastapi import FastAPI, Request, Response, WebSocket, WebSocketDisconnect, HTTPException
|
from fastapi import FastAPI, Request, Response, WebSocket, HTTPException
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
from fastapi.responses import PlainTextResponse, JSONResponse, FileResponse, RedirectResponse
|
from fastapi.responses import PlainTextResponse, JSONResponse, FileResponse, RedirectResponse
|
||||||
from jinja2.exceptions import TemplateNotFound
|
from jinja2.exceptions import TemplateNotFound
|
||||||
from .error import error_page
|
from .error import error_page
|
||||||
from .database import AccessCounter
|
from .database import AccessCounter
|
||||||
from .middleware import Middleware, server_version, onion_hostname
|
from .middleware import Middleware, server_version, onion_hostname
|
||||||
from .tools.tls_test import TlsJobQueue, TlsTestDB, tls_submit, tls_api_submit, tls_results_context
|
from .tools.tls_test import (
|
||||||
from .tools.tls_test.engine import run_full_scan
|
tls_test_db, tls_test_queue,
|
||||||
|
tls_submit, tls_api_submit, tls_results_context, tls_websocket_handler,
|
||||||
tls_test_db = TlsTestDB()
|
)
|
||||||
tls_test_queue = TlsJobQueue(tls_test_db, run_full_scan)
|
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(app: FastAPI):
|
async def lifespan(app: FastAPI):
|
||||||
@@ -179,40 +178,7 @@ async def tls_test_results_page(request: Request, test_id: str) -> Response:
|
|||||||
|
|
||||||
@app.websocket("/tools/tls-test/ws/{test_id}")
|
@app.websocket("/tools/tls-test/ws/{test_id}")
|
||||||
async def tls_test_ws(websocket: WebSocket, test_id: str):
|
async def tls_test_ws(websocket: WebSocket, test_id: str):
|
||||||
job = tls_test_db.get_job(test_id)
|
await tls_websocket_handler(websocket, test_id, tls_test_db, tls_test_queue)
|
||||||
if not job:
|
|
||||||
await websocket.close(code=4404)
|
|
||||||
return
|
|
||||||
await websocket.accept()
|
|
||||||
tls_test_queue.add_subscriber(test_id, websocket)
|
|
||||||
try:
|
|
||||||
history = tls_test_db.get_progress(test_id)
|
|
||||||
await websocket.send_text(json.dumps({
|
|
||||||
"type": "history",
|
|
||||||
"status": job.get("status"),
|
|
||||||
"target": job.get("target"),
|
|
||||||
"entries": history,
|
|
||||||
}))
|
|
||||||
if job.get("status") == "done":
|
|
||||||
await websocket.send_text(json.dumps({
|
|
||||||
"type": "done",
|
|
||||||
"redirect": f"/tools/tls-test/results/{test_id}/",
|
|
||||||
"rank": job.get("rank"),
|
|
||||||
"score": job.get("score"),
|
|
||||||
}))
|
|
||||||
await websocket.close()
|
|
||||||
return
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
await websocket.receive_text()
|
|
||||||
except WebSocketDisconnect:
|
|
||||||
break
|
|
||||||
except WebSocketDisconnect:
|
|
||||||
pass
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
finally:
|
|
||||||
tls_test_queue.remove_subscriber(test_id, websocket)
|
|
||||||
|
|
||||||
@app.api_route("/api/tools/tls-test/scan", methods=["POST"])
|
@app.api_route("/api/tools/tls-test/scan", methods=["POST"])
|
||||||
async def tls_test_api_scan(request: Request) -> Response:
|
async def tls_test_api_scan(request: Request) -> Response:
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
from .runner import TlsJobQueue
|
from .runner import TlsJobQueue
|
||||||
from .db import TlsTestDB
|
from .db import TlsTestDB
|
||||||
from .views import tls_submit, tls_api_submit, tls_results_context
|
from .engine import run_full_scan
|
||||||
|
from .views import tls_submit, tls_api_submit, tls_results_context, tls_websocket_handler
|
||||||
|
|
||||||
__all__ = ["TlsJobQueue", "TlsTestDB", "tls_submit", "tls_api_submit", "tls_results_context"]
|
tls_test_db = TlsTestDB()
|
||||||
|
tls_test_queue = TlsJobQueue(tls_test_db, run_full_scan)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"TlsJobQueue", "TlsTestDB",
|
||||||
|
"tls_submit", "tls_api_submit", "tls_results_context", "tls_websocket_handler",
|
||||||
|
"tls_test_db", "tls_test_queue",
|
||||||
|
]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
import json
|
||||||
import datetime
|
import datetime
|
||||||
from fastapi import Request
|
from fastapi import Request, WebSocket, WebSocketDisconnect
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
from fastapi.responses import Response
|
from fastapi.responses import Response
|
||||||
from .engine import validate_tls_target
|
from .engine import validate_tls_target
|
||||||
@@ -101,3 +102,39 @@ def tls_results_context(job: dict, test_id: str, request: Request, tls_test_db)
|
|||||||
"log_entries": log_entries,
|
"log_entries": log_entries,
|
||||||
"history": history,
|
"history": history,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def tls_websocket_handler(websocket: WebSocket, test_id: str, tls_test_db, tls_test_queue) -> None:
|
||||||
|
job = tls_test_db.get_job(test_id)
|
||||||
|
if not job:
|
||||||
|
await websocket.close(code=4404)
|
||||||
|
return
|
||||||
|
await websocket.accept()
|
||||||
|
tls_test_queue.add_subscriber(test_id, websocket)
|
||||||
|
try:
|
||||||
|
await websocket.send_text(json.dumps({
|
||||||
|
"type": "history",
|
||||||
|
"status": job.get("status"),
|
||||||
|
"target": job.get("target"),
|
||||||
|
"entries": tls_test_db.get_progress(test_id),
|
||||||
|
}))
|
||||||
|
if job.get("status") == "done":
|
||||||
|
await websocket.send_text(json.dumps({
|
||||||
|
"type": "done",
|
||||||
|
"redirect": f"/tools/tls-test/results/{test_id}/",
|
||||||
|
"rank": job.get("rank"),
|
||||||
|
"score": job.get("score"),
|
||||||
|
}))
|
||||||
|
await websocket.close()
|
||||||
|
return
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
await websocket.receive_text()
|
||||||
|
except WebSocketDisconnect:
|
||||||
|
break
|
||||||
|
except WebSocketDisconnect:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
tls_test_queue.remove_subscriber(test_id, websocket)
|
||||||
|
|||||||
Reference in New Issue
Block a user