diff --git a/templates/assets/js/curl.js b/templates/assets/js/curl.js index 82a0821..dc7300a 100644 --- a/templates/assets/js/curl.js +++ b/templates/assets/js/curl.js @@ -1,64 +1,64 @@ -document.addEventListener('DOMContentLoaded', function() { +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 = 'Loading...'; - - // Clear previous results - resultsContainer.innerHTML = ''; - showMessage('Fetching content...', 'info'); - - // Make the request - handleCurlRequest(url); + // 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 = '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) { + curlUrlInput.addEventListener('keyup', function (event) { if (event.key === 'Enter') { - curlButton.click(); + 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 = ` + // 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 = `

Result for ${escapeHtml(url)}

@@ -71,25 +71,27 @@ document.addEventListener('DOMContentLoaded', function() {
`; - - resultsContainer.appendChild(resultCard); - - // Get reference to the iframe and buttons - const iframe = document.getElementById('result-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 view source button - viewSourceButton.addEventListener('click', function() { - // Show source code in a modal or new window - const sourceWindow = window.open('', '_blank'); - sourceWindow.document.write(` + + resultsContainer.appendChild(resultCard); + + // Get reference to the iframe and buttons + const iframe = document.getElementById('result-iframe'); + const viewSourceButton = document.getElementById('view-source'); + + const cleanedHtml = removeScripts(data.result); + + // Create a blob URL from the cleaned HTML content + const blob = new Blob([cleanedHtml], { type: 'text/html' }); + const blobUrl = URL.createObjectURL(blob); + + // Set the iframe src to the blob URL + iframe.src = blobUrl; + + // 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(` @@ -101,80 +103,119 @@ document.addEventListener('DOMContentLoaded', function() { ${escapeHtml(data.result)} `); - sourceWindow.document.close(); + 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 = 'HTTP Request'; }); - - // 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 = 'HTTP Request'; - }); } - + + /** + * Remove all script tags and event handlers from HTML content + * @param {string} html - The HTML content + * @returns {string} - HTML content with scripts removed + */ + function removeScripts(html) { + // Create a DOM parser to work with the HTML + const parser = new DOMParser(); + const doc = parser.parseFromString(html, 'text/html'); + + // Remove all script elements + const scripts = doc.getElementsByTagName('script'); + while (scripts.length > 0) { + scripts[0].parentNode.removeChild(scripts[0]); + } + + // Remove inline event handlers from all elements + const allElements = doc.getElementsByTagName('*'); + for (let i = 0; i < allElements.length; i++) { + const element = allElements[i]; + const attrs = element.attributes; + const attrsToRemove = []; + + // Collect all event handler attributes (on*) + for (let j = 0; j < attrs.length; j++) { + if (attrs[j].name.toLowerCase().startsWith('on')) { + attrsToRemove.push(attrs[j].name); + } + } + + // Remove the collected attributes + attrsToRemove.forEach(attr => { + element.removeAttribute(attr); + }); + } + + // Return the cleaned HTML + return doc.documentElement.outerHTML; + } /** * 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); + // 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'; - } + 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; + 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 = 'Loading...'; - handleCurlRequest(url); - } + 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 = 'Loading...'; + handleCurlRequest(url); + } } - }); \ No newline at end of file +}); \ No newline at end of file