Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <title>🧠 URL Decoder & Threat Analyzer</title> | |
| <style> | |
| body { | |
| font-family: Arial, sans-serif; | |
| background-color: #111; | |
| color: #eee; | |
| padding: 2rem; | |
| } | |
| textarea, input[type="text"] { | |
| width: 100%; | |
| padding: 1rem; | |
| margin-bottom: 1rem; | |
| font-size: 1rem; | |
| border: none; | |
| border-radius: 5px; | |
| } | |
| textarea { | |
| height: 150px; | |
| resize: vertical; | |
| } | |
| button { | |
| background-color: #3c3; | |
| color: #000; | |
| border: none; | |
| padding: 1rem; | |
| margin-right: 1rem; | |
| border-radius: 5px; | |
| cursor: pointer; | |
| font-weight: bold; | |
| } | |
| button:hover { | |
| background-color: #2a2; | |
| } | |
| .output, .param-table { | |
| margin-top: 2rem; | |
| background-color: #222; | |
| padding: 1rem; | |
| border-radius: 5px; | |
| overflow-x: auto; | |
| } | |
| .param-table table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| } | |
| .param-table th, .param-table td { | |
| border: 1px solid #444; | |
| padding: 0.5rem; | |
| text-align: left; | |
| } | |
| .param-table th { | |
| background-color: #333; | |
| } | |
| .highlight { | |
| color: #ff5555; | |
| font-weight: bold; | |
| } | |
| .safe { | |
| color: #5f5; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>🧠 URL Decoder & Threat Analyzer</h1> | |
| <textarea id="input" placeholder="Вставьте один или несколько URL (через перенос строки)..."></textarea> | |
| <button onclick="processUrls()">🚀 Распознать</button> | |
| <button onclick="saveToTxt()">💾 Экспорт .txt</button> | |
| <button onclick="clearInput()">🧹 Очистить</button> | |
| <div id="results" class="output"></div> | |
| <script> | |
| function recursiveDecode(url, depth = 5) { | |
| let decoded = url; | |
| while (depth--) { | |
| try { | |
| let next = decodeURIComponent(decoded); | |
| if (next === decoded) break; | |
| decoded = next; | |
| } catch (e) { | |
| break; | |
| } | |
| } | |
| return decoded; | |
| } | |
| function extractUrlFromParams(params) { | |
| const priorityKeys = ['q', 'url', 'imgurl', 'u', 'target', 'redir', 'link']; | |
| for (let key of priorityKeys) { | |
| if (params.has(key)) { | |
| return recursiveDecode(params.get(key)); | |
| } | |
| } | |
| return null; | |
| } | |
| function isThreat(value) { | |
| const patterns = [ | |
| /eval\(/i, | |
| /base64,/i, | |
| /select.+from/i, | |
| /<script/i, | |
| /%3cscript/i, | |
| /union.*select/i, | |
| /onerror=/i, | |
| /javascript:/i | |
| ]; | |
| return patterns.some(p => p.test(value)); | |
| } | |
| function processUrls() { | |
| const rawInput = document.getElementById('input').value.trim(); | |
| const urls = rawInput.split(/\r?\n/).filter(Boolean); | |
| const results = []; | |
| let allExtracted = []; | |
| urls.forEach((raw, index) => { | |
| try { | |
| const decoded = recursiveDecode(raw); | |
| const u = new URL(decoded, window.location.origin); | |
| const params = new URLSearchParams(u.search); | |
| const extracted = extractUrlFromParams(params); | |
| const threatKeys = []; | |
| const paramArray = []; | |
| for (let [key, val] of params.entries()) { | |
| const decodedVal = recursiveDecode(val); | |
| const isDangerous = isThreat(decodedVal); | |
| if (isDangerous) threatKeys.push(key); | |
| paramArray.push(`<tr> | |
| <td>${key}</td> | |
| <td>${val}</td> | |
| <td>${decodedVal}</td> | |
| <td>${isDangerous ? '<span class="highlight">⚠️ Threat</span>' : '<span class="safe">✔️ Safe</span>'}</td> | |
| </tr>`); | |
| } | |
| results.push(` | |
| <div class="param-table"> | |
| <h3>🔗 Анализ URL #${index + 1}</h3> | |
| <p><strong>Исходный:</strong> <code>${raw}</code></p> | |
| <p><strong>Декодированный:</strong> <code>${decoded}</code></p> | |
| <p><strong>Извлечённый целевой URL:</strong> ${extracted ? `<a href="${extracted}" target="_blank">${extracted}</a>` : '—'}</p> | |
| <p><strong>Угрозы:</strong> ${threatKeys.length > 0 ? '<span class="highlight">' + threatKeys.join(', ') + '</span>' : '<span class="safe">Нет</span>'}</p> | |
| <table> | |
| <thead> | |
| <tr><th>Ключ</th><th>Raw</th><th>Decoded</th><th>Статус</th></tr> | |
| </thead> | |
| <tbody> | |
| ${paramArray.join('')} | |
| </tbody> | |
| </table> | |
| </div> | |
| `); | |
| if (extracted) allExtracted.push(extracted); | |
| } catch (e) { | |
| results.push(`<div class="param-table"><h3>Ошибка обработки URL #${index + 1}</h3><p>${raw}</p><p><span class="highlight">${e.message}</span></p></div>`); | |
| } | |
| }); | |
| document.getElementById('results').innerHTML = results.join('\n'); | |
| window.lastExtractedUrls = allExtracted; | |
| } | |
| function saveToTxt() { | |
| if (!window.lastExtractedUrls || window.lastExtractedUrls.length === 0) { | |
| alert("Нет URL для экспорта."); | |
| return; | |
| } | |
| const blob = new Blob([window.lastExtractedUrls.join('\n')], { type: "text/plain" }); | |
| const a = document.createElement("a"); | |
| a.href = URL.createObjectURL(blob); | |
| a.download = "extracted_urls.txt"; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| } | |
| function clearInput() { | |
| document.getElementById("input").value = ""; | |
| document.getElementById("results").innerHTML = ""; | |
| window.lastExtractedUrls = []; | |
| } | |
| document.addEventListener("keydown", function(e) { | |
| if (e.ctrlKey && e.key === "Enter") { | |
| e.preventDefault(); | |
| processUrls(); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |