diff --git a/Chrome/Code/background.js b/Chrome/Code/background.js new file mode 100644 index 0000000..64ad560 --- /dev/null +++ b/Chrome/Code/background.js @@ -0,0 +1,29 @@ +// background.js - Background script for handling downloads (Chrome-optimized) + +chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + if (request.action === "downloadSQL") { + const { sqlQuery, filename } = request; + + // Create blob with SQL content + const blob = new Blob([sqlQuery], { type: 'text/sql' }); + const url = URL.createObjectURL(blob); + + // Generate filename if not provided + const finalFilename = filename || `sql_query_${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.sql`; + + // Download the file + chrome.downloads.download({ + url: url, + filename: finalFilename, + saveAs: true + }).then(() => { + URL.revokeObjectURL(url); + sendResponse({ success: true }); + }).catch((error) => { + console.error('Download failed:', error); + sendResponse({ success: false, error: error.message }); + }); + + return true; // Keep the message channel open for async response + } +}); diff --git a/Chrome/Code/content.js b/Chrome/Code/content.js new file mode 100644 index 0000000..e23426f --- /dev/null +++ b/Chrome/Code/content.js @@ -0,0 +1,89 @@ +// content.js - Content script that runs on web pages (Chrome-optimized) + +function extractSQLQuery() { + // Look for the specific input element + const sqlInput = document.getElementById('SQLStatementHide'); + + if (sqlInput) { + const sqlQuery = sqlInput.value; + if (sqlQuery && sqlQuery.trim()) { + return cleanupSQLQuery(sqlQuery.trim()); + } else { + return null; + } + } + + // Fallback: look for any input with name="SQLStatementHide" + const sqlInputByName = document.querySelector('input[name="SQLStatementHide"]'); + if (sqlInputByName) { + const sqlQuery = sqlInputByName.value; + if (sqlQuery && sqlQuery.trim()) { + return cleanupSQLQuery(sqlQuery.trim()); + } + } + + return null; +} + +function cleanupSQLQuery(sqlQuery) { + // Fix double single quotes around dates/values (''2017-10-23'' becomes '2017-10-23') + // This handles the common issue where systems incorrectly double-escape quotes + return sqlQuery.replace(/''/g, "'"); +} + +// Listen for messages from popup +chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + if (request.action === "extractSQL") { + const sqlQuery = extractSQLQuery(); + sendResponse({ + success: sqlQuery !== null, + sqlQuery: sqlQuery, + url: window.location.href, + timestamp: new Date().toISOString() + }); + } +}); + +// Optional: Add a visual indicator when SQL is found +function addVisualIndicator() { + const sqlInput = document.getElementById('SQLStatementHide') || + document.querySelector('input[name="SQLStatementHide"]'); + + if (sqlInput && sqlInput.value && sqlInput.value.trim()) { + // Add a small visual indicator that SQL was found + if (!document.getElementById('sql-extractor-indicator')) { + const indicator = document.createElement('div'); + indicator.id = 'sql-extractor-indicator'; + indicator.style.cssText = ` + position: fixed; + top: 10px; + right: 10px; + background: #4CAF50; + color: white; + padding: 5px 10px; + border-radius: 3px; + font-size: 12px; + z-index: 10000; + font-family: Arial, sans-serif; + opacity: 0.8; + pointer-events: none; + `; + indicator.textContent = 'SQL Query Found'; + document.body.appendChild(indicator); + + // Remove indicator after 3 seconds + setTimeout(() => { + if (document.getElementById('sql-extractor-indicator')) { + document.body.removeChild(indicator); + } + }, 3000); + } + } +} + +// Run indicator check when page loads +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', addVisualIndicator); +} else { + addVisualIndicator(); +} diff --git a/Chrome/Code/icon128.png b/Chrome/Code/icon128.png new file mode 100644 index 0000000..fc641a6 Binary files /dev/null and b/Chrome/Code/icon128.png differ diff --git a/Chrome/Code/icon16.png b/Chrome/Code/icon16.png new file mode 100644 index 0000000..965af12 Binary files /dev/null and b/Chrome/Code/icon16.png differ diff --git a/Chrome/Code/icon32.png b/Chrome/Code/icon32.png new file mode 100644 index 0000000..1241c96 Binary files /dev/null and b/Chrome/Code/icon32.png differ diff --git a/Chrome/Code/icon48.png b/Chrome/Code/icon48.png new file mode 100644 index 0000000..ac59f13 Binary files /dev/null and b/Chrome/Code/icon48.png differ diff --git a/Chrome/Code/manifest.json b/Chrome/Code/manifest.json new file mode 100644 index 0000000..22d3079 --- /dev/null +++ b/Chrome/Code/manifest.json @@ -0,0 +1,40 @@ +{ + "manifest_version": 3, + "name": "SimplyReports SQL Extractor", + "version": "1.0", + "description": "Grab the SQL query used in a SimplyReports results page and copy it to the clipboard or save it to a file.", + + "permissions": [ + "activeTab", + "downloads" + ], + + "content_scripts": [ + { + "matches": [""], + "js": ["content.js"] + } + ], + + "background": { + "service_worker": "background.js" + }, + + "action": { + "default_popup": "popup.html", + "default_title": "Extract SQL Query", + "default_icon": { + "16": "icon16.png", + "32": "icon32.png", + "48": "icon48.png", + "128": "icon128.png" + } + }, + + "icons": { + "16": "icon16.png", + "32": "icon32.png", + "48": "icon48.png", + "128": "icon128.png" + } +} diff --git a/Chrome/Code/popup.html b/Chrome/Code/popup.html new file mode 100644 index 0000000..72ba633 --- /dev/null +++ b/Chrome/Code/popup.html @@ -0,0 +1,149 @@ + + + + + + + +
+

SiReS Ex - SimplyReports SQL Extractor

+
+ + + + + +
+ + + +
+ +
+ Click "Extract SQL Query" to search for SQL in the current page +
+ + + + diff --git a/Chrome/Code/popup.js b/Chrome/Code/popup.js new file mode 100644 index 0000000..83bebea --- /dev/null +++ b/Chrome/Code/popup.js @@ -0,0 +1,182 @@ +// popup.js - Popup script for user interactions (Manifest V3 compatible) + +let currentSQLQuery = null; + +document.addEventListener('DOMContentLoaded', function() { + const extractBtn = document.getElementById('extractBtn'); + const copyBtn = document.getElementById('copyBtn'); + const downloadBtn = document.getElementById('downloadBtn'); + const filenameInput = document.getElementById('filenameInput'); + const statusDiv = document.getElementById('status'); + const sqlPreview = document.getElementById('sqlPreview'); + const actionButtons = document.getElementById('actionButtons'); + + // Extract SQL Query + extractBtn.addEventListener('click', async function() { + extractBtn.disabled = true; + extractBtn.textContent = 'Extracting...'; + + try { + // Get active tab + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + + // Send message to content script + const response = await chrome.tabs.sendMessage(tab.id, { action: "extractSQL" }); + + extractBtn.disabled = false; + extractBtn.textContent = 'Extract SQL Query'; + + if (response && response.success) { + // Debug: Log the original SQL + console.log('Original SQL:', response.sqlQuery); + + // Store the formatted version as the main query + currentSQLQuery = formatSQL(response.sqlQuery); + + // Debug: Log the formatted SQL + console.log('Formatted SQL:', currentSQLQuery); + console.log('Formatted SQL contains newlines:', currentSQLQuery.includes('\n')); + + showStatus('success', 'SQL Query found and extracted!'); + showSQLPreview(currentSQLQuery); + actionButtons.style.display = 'block'; + + // Set default filename based on current page + const url = new URL(response.url); + const defaultName = `sql_query_${url.hostname}_${new Date().toISOString().slice(0, 10)}.sql`; + filenameInput.value = defaultName; + } else { + showStatus('error', 'No SQL query found in the current page'); + actionButtons.style.display = 'none'; + sqlPreview.style.display = 'none'; + currentSQLQuery = null; + } + } catch (error) { + extractBtn.disabled = false; + extractBtn.textContent = 'Extract SQL Query'; + showStatus('error', 'Error extracting SQL: ' + error.message); + } + }); + + // Copy to Clipboard + copyBtn.addEventListener('click', async function() { + if (!currentSQLQuery) return; + + try { + // Debug: Log what we're trying to copy + console.log('Copying SQL:', currentSQLQuery); + console.log('SQL length:', currentSQLQuery.length); + console.log('Contains newlines:', currentSQLQuery.includes('\n')); + + await navigator.clipboard.writeText(currentSQLQuery); + + const originalText = copyBtn.textContent; + copyBtn.textContent = 'Copied!'; + copyBtn.style.backgroundColor = '#28a745'; + + setTimeout(function() { + copyBtn.textContent = originalText; + copyBtn.style.backgroundColor = ''; + }, 1500); + } catch (err) { + showStatus('error', 'Failed to copy to clipboard: ' + err.message); + } + }); + + // Download as File + downloadBtn.addEventListener('click', async function() { + if (!currentSQLQuery) return; + + downloadBtn.disabled = true; + downloadBtn.textContent = 'Downloading...'; + + try { + const filename = filenameInput.value.trim() || null; + + // Send message to background script + const response = await chrome.runtime.sendMessage({ + action: "downloadSQL", + sqlQuery: currentSQLQuery, + filename: filename + }); + + downloadBtn.disabled = false; + downloadBtn.textContent = 'Save as File'; + + if (response && response.success) { + const originalText = downloadBtn.textContent; + downloadBtn.textContent = 'Downloaded!'; + downloadBtn.style.backgroundColor = '#28a745'; + + setTimeout(function() { + downloadBtn.textContent = originalText; + downloadBtn.style.backgroundColor = ''; + }, 1500); + } else { + showStatus('error', 'Failed to download file: ' + (response ? response.error : 'Unknown error')); + } + } catch (error) { + downloadBtn.disabled = false; + downloadBtn.textContent = 'Save as File'; + showStatus('error', 'Failed to download file: ' + error.message); + } + }); + + function showStatus(type, message) { + statusDiv.className = `status ${type}`; + statusDiv.textContent = message; + statusDiv.style.display = 'block'; + + // Auto-hide after 5 seconds + setTimeout(function() { + statusDiv.style.display = 'none'; + }, 5000); + } + + function showSQLPreview(sqlQuery) { + sqlPreview.textContent = sqlQuery; + sqlPreview.style.display = 'block'; + } + + function formatSQL(sql) { + // Simple but reliable SQL formatting + let formatted = sql.trim(); + + // Normalize whitespace first + formatted = formatted.replace(/\s+/g, ' '); + + // Add line breaks before major keywords (simple approach) + formatted = formatted.replace(/\sFROM\s/gi, '\nFROM '); + formatted = formatted.replace(/\sWHERE\s/gi, '\nWHERE '); + formatted = formatted.replace(/\sAND\s/gi, '\n AND '); + formatted = formatted.replace(/\sOR\s/gi, '\n OR '); + formatted = formatted.replace(/\sORDER\sBY\s/gi, '\nORDER BY '); + formatted = formatted.replace(/\sGROUP\sBY\s/gi, '\nGROUP BY '); + formatted = formatted.replace(/\sHAVING\s/gi, '\nHAVING '); + formatted = formatted.replace(/\sJOIN\s/gi, '\nJOIN '); + formatted = formatted.replace(/\sINNER\sJOIN\s/gi, '\nINNER JOIN '); + formatted = formatted.replace(/\sLEFT\sJOIN\s/gi, '\nLEFT JOIN '); + formatted = formatted.replace(/\sRIGHT\sJOIN\s/gi, '\nRIGHT JOIN '); + + // Add semicolon if missing + if (!formatted.trim().endsWith(';')) { + formatted += ';'; + } + + // Clean up any leading/trailing whitespace on lines + formatted = formatted.split('\n').map(line => line.trim()).join('\n'); + + return formatted; + } + + // Auto-extract on popup open if we're on a page that might have SQL + chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) { + const currentTab = tabs[0]; + if (currentTab.url && !currentTab.url.startsWith('chrome://') && !currentTab.url.startsWith('chrome-extension://')) { + // Auto-extract after a short delay + setTimeout(function() { + extractBtn.click(); + }, 500); + } + }); +}); diff --git a/Chrome/Privacy Policy/Privacy Policy for SiReS Ex.md b/Chrome/Privacy Policy/Privacy Policy for SiReS Ex.md new file mode 100644 index 0000000..6f0bd27 --- /dev/null +++ b/Chrome/Privacy Policy/Privacy Policy for SiReS Ex.md @@ -0,0 +1,45 @@ +# Privacy Policy for SimplyReports SQL Extractor + +--- + +**Last updated: 2025-10-27** + +## Information Collection and Use + +SimplyReports SQL Extractor does **not collect, store, or transmit any personal information or user data**. This extension operates entirely within your local browser environment. + +## What This Extension Does + +This extension: + +- Scans the current web page for SQL queries in specific HTML elements +- Displays found SQL queries within the extension popup +- Allows you to copy SQL queries to your clipboard +- Enables you to download SQL queries as local files + +## Data Processing + +- **No External Servers**: All processing happens locally in your browser +- **No Data Storage**: No user data, browsing history, or personal information is stored by this extension +- **No Data Transmission**: No information is sent to external servers or third parties +- **Temporary Processing Only**: SQL queries are only held temporarily in memory while you use the extension + +## Permissions Used + +This extension requests the following permissions: + +- **activeTab**: To read content from the current web page to extract SQL queries +- **clipboardWrite**: To copy SQL queries to your clipboard when requested +- **downloads**: To save SQL query files to your local computer when requested + +## Third-Party Services + +This extension does not integrate with or send data to any third-party services, analytics platforms, or external APIs. + +## Changes to This Policy + +Any updates to this privacy policy will be reflected in the extension's listing on the Chrome Web Store. + +## Contact + +If you have questions about this privacy policy, please contact cyberpunklibrarian [at] protonmail [dot] com \ No newline at end of file