A WebCrawler for iOS Safari with proxy support
// ==UserScript==
// @name iOS Safari WebCrawler
// @namespace http://tampermonkey.net/
// @version 1.0
// @description A WebCrawler for iOS Safari with proxy support
// @author SiJosxStudio
// @url http://tinyurl.com/BuySijosxStudioCoffee
// @match *://*/*
// @grant GM_download
// @license MIT
// ==/UserScript==
(function () {
'use strict';
class WebCrawler {
constructor() {
this.url = '';
this.proxy = '';
this.findings = [];
}
async startCrawl(url, proxy) {
this.validateInput(url, proxy);
this.url = url;
this.proxy = proxy;
try {
const response = await fetch(this.proxy + '/' + this.url);
if (!response.ok) {
throw new Error(`Failed to fetch: ${response.statusText}`);
}
const text = await response.text();
this.extractFindings(text);
} catch (error) {
console.error('Error during crawling:', error);
throw new Error('Crawling failed: ' + error.message);
}
}
validateInput(url, proxy) {
const urlPattern = new RegExp('^(https:\\/\\/)' +
'((([a-z\\d]([a-z\\d-]*[a-z\\d])?)\\.)+[a-z]{2,}|localhost|' +
'\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|' +
'\\[?[a-fA-F0-9]*:[a-fA-F0-9:]+\\]?)' +
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' +
'(\\[;&a-z\\d%_.~+=-]*)' +
'(\\#[-a-z\\d_]*)$', 'i');
if (!urlPattern.test(url)) {
throw new Error('Invalid URL format.');
}
if (!proxy) {
throw new Error('Proxy cannot be empty.');
}
}
extractFindings(html) {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const title = doc.querySelector('title') ? doc.querySelector('title').innerText : 'No title found';
this.findings.push(`Title: ${title}`);
const metaTags = doc.querySelectorAll('meta');
metaTags.forEach(meta => {
const name = meta.getAttribute('name') || 'unknown';
const content = meta.getAttribute('content') || 'no content';
this.findings.push(`Meta: ${name} = ${content}`);
});
const links = doc.querySelectorAll('a');
links.forEach(link => {
const href = link.getAttribute('href') || 'no href';
this.findings.push(`Link: ${href}`);
});
}
saveFindingsToFile(filename) {
const blob = new Blob([this.findings.join('\n')], { type: 'text/plain' });
if (typeof GM_download !== 'undefined') {
// Use GM_download if available (in Tampermonkey for iOS)
GM_download(URL.createObjectURL(blob), filename);
} else {
// For Safari fallback
const reader = new FileReader();
reader.onloadend = function () {
window.open(reader.result, '_blank');
};
reader.readAsDataURL(blob);
}
}
}
// Add buttons to the webpage for user interaction
function createButton(id, label, onClick) {
const button = document.createElement('button');
button.id = id;
button.innerText = label;
button.style.position = 'fixed';
button.style.top = `${50 + document.querySelectorAll('button').length * 40}px`;
button.style.right = '10px';
button.style.zIndex = '9999';
button.onclick = onClick;
document.body.appendChild(button);
}
const crawler = new WebCrawler();
createButton('startCrawlButton', 'Start Crawl', async () => {
const url = prompt("Enter the URL to crawl");
const proxy = prompt("Enter the proxy to use");
try {
await crawler.startCrawl(url, proxy);
console.log('Crawling completed:', crawler.findings);
alert("Crawling completed! Check console for findings.");
} catch (error) {
console.error('Crawling error:', error.message);
alert("Crawling error: " + error.message);
}
});
createButton('saveFindingsButton', 'Save Findings', () => {
const filename = 'findings.txt';
crawler.saveFindingsToFile(filename);
});
})();