generated from nathanwoodburn/python-webserver-template
This commit is contained in:
@@ -17,6 +17,7 @@ from datetime import datetime
|
|||||||
import dotenv
|
import dotenv
|
||||||
import re
|
import re
|
||||||
import tools
|
import tools
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
dotenv.load_dotenv()
|
dotenv.load_dotenv()
|
||||||
|
|
||||||
@@ -112,6 +113,13 @@ def api_ssl(domain: str):
|
|||||||
result = tools.check_ssl(domain)
|
result = tools.check_ssl(domain)
|
||||||
return jsonify(result)
|
return jsonify(result)
|
||||||
|
|
||||||
|
@app.route("/api/v1/curl/<path:url>")
|
||||||
|
def api_curl(url: str):
|
||||||
|
# Decode the URL
|
||||||
|
url = urllib.parse.unquote(url)
|
||||||
|
|
||||||
|
result = tools.curl(url)
|
||||||
|
return jsonify(result)
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
|
|||||||
190
templates/assets/js/curl.js
Normal file
190
templates/assets/js/curl.js
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Get references to elements
|
||||||
|
const curlButton = document.getElementById('curl');
|
||||||
|
const curlUrlInput = document.getElementById('curl-url');
|
||||||
|
const resultsContainer = document.getElementById('curl-results');
|
||||||
|
|
||||||
|
// Add click event listener to the button
|
||||||
|
curlButton.addEventListener('click', function() {
|
||||||
|
// Get the URL from the input
|
||||||
|
const url = curlUrlInput.value.trim();
|
||||||
|
|
||||||
|
// Validate URL
|
||||||
|
if (!url) {
|
||||||
|
showMessage('Please enter a URL', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the url
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append('url', url);
|
||||||
|
history.pushState(null, null, `?${params.toString()}`);
|
||||||
|
|
||||||
|
// Show loading state
|
||||||
|
curlButton.disabled = true;
|
||||||
|
curlButton.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>Loading...';
|
||||||
|
|
||||||
|
// Clear previous results
|
||||||
|
resultsContainer.innerHTML = '';
|
||||||
|
showMessage('Fetching content...', 'info');
|
||||||
|
|
||||||
|
// Make the request
|
||||||
|
handleCurlRequest(url);
|
||||||
|
});
|
||||||
|
// Add enter key listener to input
|
||||||
|
curlUrlInput.addEventListener('keyup', function(event) {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
curlButton.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the curl API request
|
||||||
|
* @param {string} url - The URL to curl
|
||||||
|
*/
|
||||||
|
function handleCurlRequest(url) {
|
||||||
|
// Encode the URL for the API endpoint
|
||||||
|
const encodedUrl = encodeURIComponent(url);
|
||||||
|
const apiEndpoint = `/api/v1/curl/${encodedUrl}`;
|
||||||
|
|
||||||
|
fetch(apiEndpoint)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
// Clear loading message
|
||||||
|
resultsContainer.innerHTML = '';
|
||||||
|
|
||||||
|
if (data.success === true) {
|
||||||
|
// Create a result card with iframe
|
||||||
|
const resultCard = document.createElement('div');
|
||||||
|
resultCard.className = 'card shadow border-0 rounded-lg mb-5';
|
||||||
|
|
||||||
|
resultCard.innerHTML = `
|
||||||
|
<div class="card-header bg-success text-white py-3 d-flex justify-content-between align-items-center">
|
||||||
|
<h3 class="h5 mb-0">Result for ${escapeHtml(url)}</h3>
|
||||||
|
<div>
|
||||||
|
<button class="btn btn-sm btn-light" id="refresh-iframe">
|
||||||
|
<i class="bi bi-arrow-clockwise me-1"></i>Refresh
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-light ms-2" id="view-source">
|
||||||
|
<i class="bi bi-code-slash me-1"></i>View Source
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<iframe id="result-iframe" style="width:100%; height:600px; border:0;"></iframe>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
resultsContainer.appendChild(resultCard);
|
||||||
|
|
||||||
|
// Get reference to the iframe and buttons
|
||||||
|
const iframe = document.getElementById('result-iframe');
|
||||||
|
const refreshButton = document.getElementById('refresh-iframe');
|
||||||
|
const viewSourceButton = document.getElementById('view-source');
|
||||||
|
|
||||||
|
// Create a blob URL from the HTML content
|
||||||
|
const blob = new Blob([data.result], { type: 'text/html' });
|
||||||
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
// Set the iframe src to the blob URL
|
||||||
|
iframe.src = blobUrl;
|
||||||
|
|
||||||
|
// Add event listener to refresh button
|
||||||
|
refreshButton.addEventListener('click', function() {
|
||||||
|
// Reload the content
|
||||||
|
handleCurlRequest(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add event listener to view source button
|
||||||
|
viewSourceButton.addEventListener('click', function() {
|
||||||
|
// Show source code in a modal or new window
|
||||||
|
const sourceWindow = window.open('', '_blank');
|
||||||
|
sourceWindow.document.write(`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Source for ${escapeHtml(url)}</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: monospace; white-space: pre-wrap; padding: 20px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>${escapeHtml(data.result)}</body>
|
||||||
|
</html>
|
||||||
|
`);
|
||||||
|
sourceWindow.document.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set up cleanup when iframe is no longer needed
|
||||||
|
window.addEventListener('beforeunload', function() {
|
||||||
|
URL.revokeObjectURL(blobUrl);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Show error message
|
||||||
|
showMessage(data.error || 'Failed to get content', 'error');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
showMessage(`Request failed: ${error.message}`, 'error');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
// Reset button state
|
||||||
|
curlButton.disabled = false;
|
||||||
|
curlButton.innerHTML = '<i class="bi bi-shield-check me-2"></i>HTTP Request';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a message in the results container
|
||||||
|
* @param {string} message - The message to display
|
||||||
|
* @param {string} type - Message type (error, success, info)
|
||||||
|
*/
|
||||||
|
function showMessage(message, type) {
|
||||||
|
// Clear previous content
|
||||||
|
resultsContainer.innerHTML = '';
|
||||||
|
|
||||||
|
// Create alert element
|
||||||
|
const alert = document.createElement('div');
|
||||||
|
alert.className = `alert alert-${getAlertClass(type)} shadow-sm`;
|
||||||
|
alert.innerHTML = message;
|
||||||
|
|
||||||
|
// Add the alert to the results container
|
||||||
|
resultsContainer.appendChild(alert);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Bootstrap alert class based on message type
|
||||||
|
* @param {string} type - Message type
|
||||||
|
* @returns {string} - Bootstrap alert class
|
||||||
|
*/
|
||||||
|
function getAlertClass(type) {
|
||||||
|
switch (type) {
|
||||||
|
case 'error': return 'danger';
|
||||||
|
case 'success': return 'success';
|
||||||
|
case 'info': default: return 'info';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape HTML special characters to prevent XSS
|
||||||
|
* @param {string} text - Text to escape
|
||||||
|
* @returns {string} - Escaped text
|
||||||
|
*/
|
||||||
|
function escapeHtml(text) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.textContent = text;
|
||||||
|
return div.innerHTML;
|
||||||
|
}
|
||||||
|
// Check if params are present
|
||||||
|
if (window.location.search) {
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const url = params.get('url');
|
||||||
|
if (url) {
|
||||||
|
// Add url to input
|
||||||
|
curlUrlInput.value = url;
|
||||||
|
// Show loading state
|
||||||
|
curlButton.disabled = true;
|
||||||
|
curlButton.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>Loading...';
|
||||||
|
handleCurlRequest(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="/assets/css/index.css">
|
<link rel="stylesheet" href="/assets/css/index.css">
|
||||||
<script src="/assets/js/ssl.js" defer></script>
|
<script src="/assets/js/ssl.js" defer></script>
|
||||||
<script src="/assets/js/generator.js" defer></script>
|
<script src="/assets/js/curl.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
<div class="border-bottom mt-4 w-50 mx-auto"></div>
|
<div class="border-bottom mt-4 w-50 mx-auto"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tool Card -->
|
<!-- SSL check Tool Card -->
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<div class="card shadow border-0 rounded-lg mb-5">
|
<div class="card shadow border-0 rounded-lg mb-5">
|
||||||
@@ -54,6 +54,40 @@
|
|||||||
<div id="ssl-results" class="animate__animated animate__fadeIn"></div>
|
<div id="ssl-results" class="animate__animated animate__fadeIn"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- CURL Tool Card -->
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card shadow border-0 rounded-lg mb-5">
|
||||||
|
<div class="card-header bg-primary text-white py-3">
|
||||||
|
<h2 class="h4 mb-0 text-center">Curl Website</h2>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-4">
|
||||||
|
<p class="text-muted mb-4 text-center">Do a http request for a domain</p>
|
||||||
|
|
||||||
|
<div class="input-group mb-3 shadow-sm">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="curl-url"
|
||||||
|
placeholder="Enter url (e.g., https://nathan.woodburn http://firewallet)"
|
||||||
|
class="form-control form-control-lg"
|
||||||
|
aria-label="URL to check"
|
||||||
|
>
|
||||||
|
<button class="btn btn-primary px-4" id="curl">
|
||||||
|
<i class="bi bi-shield-check me-2"></i>HTTP Request
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center text-muted small">
|
||||||
|
Enter a complete url including the http or https protocol
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Results Container -->
|
||||||
|
<div id="curl-results" class="animate__animated animate__fadeIn"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<footer class="text-center mt-5 pt-4 text-muted">
|
<footer class="text-center mt-5 pt-4 text-muted">
|
||||||
|
|||||||
25
tools.py
25
tools.py
@@ -6,8 +6,8 @@ import binascii
|
|||||||
from cryptography import x509
|
from cryptography import x509
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
import datetime
|
import datetime
|
||||||
from dns import resolver, dnssec, name, exception
|
from dns import resolver
|
||||||
import time
|
import requests
|
||||||
|
|
||||||
resolver = dns.resolver.Resolver()
|
resolver = dns.resolver.Resolver()
|
||||||
resolver.nameservers = ["194.50.5.28","194.50.5.27","194.50.5.26"]
|
resolver.nameservers = ["194.50.5.28","194.50.5.27","194.50.5.26"]
|
||||||
@@ -170,4 +170,23 @@ def validate_dnssec(domain):
|
|||||||
if "; fully validated" in result.stdout or "; negative response, fully validated" in result.stdout:
|
if "; fully validated" in result.stdout or "; negative response, fully validated" in result.stdout:
|
||||||
return {"valid": True, "message": "DNSSEC is valid", "output": result.stderr + result.stdout}
|
return {"valid": True, "message": "DNSSEC is valid", "output": result.stderr + result.stdout}
|
||||||
else:
|
else:
|
||||||
return {"valid": False, "message": "DNSSEC is not valid", "output": result.stderr + result.stdout}
|
return {"valid": False, "message": "DNSSEC is not valid", "output": result.stderr + result.stdout}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def curl(url: str):
|
||||||
|
if not url.startswith("http"):
|
||||||
|
url = "http://" + url
|
||||||
|
try:
|
||||||
|
# curl --doh-url https://hnsdoh.com/dns-query {url} --insecure
|
||||||
|
commmand = f"curl --doh-url https://hnsdoh.com/dns-query {url} --insecure --silent"
|
||||||
|
response = subprocess.run(commmand, shell=True, capture_output=True, text=True)
|
||||||
|
if response.returncode != 0:
|
||||||
|
return {"success": False, "error": response.stderr}
|
||||||
|
else:
|
||||||
|
return {"success": True, "result": response.stdout}
|
||||||
|
except:
|
||||||
|
return {"success": False, "error": "An error occurred"}
|
||||||
|
|
||||||
|
# if __name__ == "__main__":
|
||||||
|
# print(curl("https://dso.dprofile"))
|
||||||
Reference in New Issue
Block a user