From 4739a1377c9f02e81fd7422fa8ea8131cc61df4c Mon Sep 17 00:00:00 2001 From: martin legrand Date: Wed, 14 May 2025 18:50:40 +0200 Subject: [PATCH 01/10] install: improve sh scripts --- scripts/linux_install.sh | 30 +++++++++++++++++++++--------- scripts/macos_install.sh | 27 ++++++++++++++++++++------- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/scripts/linux_install.sh b/scripts/linux_install.sh index 395c52e..0cdec3c 100755 --- a/scripts/linux_install.sh +++ b/scripts/linux_install.sh @@ -3,10 +3,22 @@ echo "Starting installation for Linux..." set -e + +if ! command -v python3.10 &> /dev/null; then + echo "Error: Python 3.10 is not installed. Please install Python 3.10 and try again." + echo "You can install it using: sudo apt-get install python3.10 python3.10-dev python3.10-venv" + exit 1 +fi + +# Check if pip3.10 is available +if ! python3.10 -m pip --version &> /dev/null; then + echo "Error: pip for Python 3.10 is not installed. Installing python3.10-pip..." + sudo apt-get install -y python3.10-pip || { echo "Failed to install python3.10-pip"; exit 1; } +fi + # Update package list sudo apt-get update || { echo "Failed to update package list"; exit 1; } # make sure essential tool are installed -# Install essential tools sudo apt-get install -y \ python3-dev \ python3-pip \ @@ -21,15 +33,15 @@ sudo apt-get install -y \ libnss3 \ libxss1 || { echo "Failed to install packages"; exit 1; } -# upgrade pip -pip install --upgrade pip -# install wheel -pip install --upgrade pip setuptools wheel +# Upgrade pip for Python 3.10 +python3.10 -m pip install --upgrade pip || { echo "Failed to upgrade pip"; exit 1; } +# Install and upgrade setuptools and wheel +python3.10 -m pip install --upgrade setuptools wheel || { echo "Failed to install setuptools and wheel"; exit 1; } +# Install Selenium for chromedriver +python3.10 -m pip install selenium || { echo "Failed to install selenium"; exit 1; } +# Install Python dependencies from requirements.txt +python3.10 -m pip install -r requirements.txt --no-cache-dir || { echo "Failed to install requirements.txt"; exit 1; } # install docker compose sudo apt install -y docker-compose -# Install Selenium for chromedriver -pip3 install selenium -# Install Python dependencies from requirements.txt -pip3 install -r requirements.txt --no-cache-dir echo "Installation complete for Linux!" \ No newline at end of file diff --git a/scripts/macos_install.sh b/scripts/macos_install.sh index 51ec466..9b716be 100755 --- a/scripts/macos_install.sh +++ b/scripts/macos_install.sh @@ -4,6 +4,18 @@ echo "Starting installation for macOS..." set -e +if ! command -v python3.10 &> /dev/null; then + echo "Error: Python 3.10 is not installed. Please install Python 3.10 and try again." + echo "You can install it using: sudo apt-get install python3.10 python3.10-dev python3.10-venv" + exit 1 +fi + +# Check if pip3.10 is available +if ! python3.10 -m pip --version &> /dev/null; then + echo "Error: pip for Python 3.10 is not installed. Installing python3.10-pip..." + sudo apt-get install -y python3.10-pip || { echo "Failed to install python3.10-pip"; exit 1; } +fi + # Check if homebrew is installed if ! command -v brew &> /dev/null; then echo "Homebrew not found. Installing Homebrew..." @@ -18,13 +30,14 @@ brew install wget brew install --cask chromedriver # Install portaudio for pyAudio using Homebrew brew install portaudio -# update pip -python3 -m pip install --upgrade pip -# upgrade setuptools and wheel -pip3 install --upgrade setuptools wheel -# Install Selenium -pip3 install selenium + +# Upgrade pip for Python 3.10 +python3.10 -m pip install --upgrade pip || { echo "Failed to upgrade pip"; exit 1; } +# Install and upgrade setuptools and wheel +python3.10 -m pip install --upgrade setuptools wheel || { echo "Failed to install setuptools and wheel"; exit 1; } +# Install Selenium for chromedriver +python3.10 -m pip install selenium || { echo "Failed to install selenium"; exit 1; } # Install Python dependencies from requirements.txt -pip3 install -r requirements.txt --no-cache-dir +python3.10 -m pip install -r requirements.txt --no-cache-dir || { echo "Failed to install requirements.txt"; exit 1; } echo "Installation complete for macOS!" \ No newline at end of file From 201b3de15ce89aaf5965587f7e846fd0fdfccfd5 Mon Sep 17 00:00:00 2001 From: martin legrand Date: Wed, 14 May 2025 21:43:13 +0200 Subject: [PATCH 02/10] feat : attempt to bypass bot detection --- sources/browser.py | 83 +++++++-- sources/web_scripts/inject_safety_script.js | 4 +- sources/web_scripts/spoofing.js | 194 ++++++++++++++++++++ 3 files changed, 266 insertions(+), 15 deletions(-) create mode 100644 sources/web_scripts/spoofing.js diff --git a/sources/browser.py b/sources/browser.py index 15b6517..2bb7b92 100644 --- a/sources/browser.py +++ b/sources/browser.py @@ -62,9 +62,9 @@ def get_chrome_path() -> str: def get_random_user_agent() -> str: """Get a random user agent string with associated vendor.""" user_agents = [ - {"ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36", "vendor": "Google Inc."}, - {"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_6_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15", "vendor": "Apple Inc."}, - {"ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "vendor": ""}, + {"ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36", "vendor": "Google Inc."}, + {"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_6_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36", "vendor": "Apple Inc."}, + {"ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36", "vendor": "Google Inc."}, ] return random.choice(user_agents) @@ -91,7 +91,7 @@ def bypass_ssl() -> str: """ This is a fallback for stealth mode to bypass SSL verification. Which can fail on some setup. """ - pretty_print("This is a workaround for SSL issues but upsafe we strongly advice you update your certifi SSL certificate.", color="warning") + pretty_print("Bypassing SSL verification issues, we strongly advice you update your certifi SSL certificate.", color="warning") ssl._create_default_https_context = ssl._create_unverified_context def create_undetected_chromedriver(service, chrome_options) -> webdriver.Chrome: @@ -107,7 +107,6 @@ def create_undetected_chromedriver(service, chrome_options) -> webdriver.Chrome: pretty_print(f"Failed to create Chrome driver, fallback failed:\n{str(e)}.", color="failure") raise e raise e - # hide webdriver flag driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})") return driver @@ -122,16 +121,28 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx if headless: chrome_options.add_argument("--headless") - chrome_options.add_argument("--disable-gpu") - chrome_options.add_argument("--disable-webgl") + chrome_options.add_argument("--use-gl=swiftshader") + #chrome_options.add_argument("--disable-gpu") + #chrome_options.add_argument("--disable-webgl") # prevent some website from working, commented for now user_data_dir = tempfile.mkdtemp() user_agent = get_random_user_agent() chrome_options.add_argument(f"--user-data-dir={user_data_dir}") + chrome_options.add_argument("--use-gl=egl") + chrome_options.add_argument("--enable-webgl") + chrome_options.add_argument("--enable-3d-apis") chrome_options.add_argument("--no-sandbox") chrome_options.add_argument("--disable-dev-shm-usage") chrome_options.add_argument("--mute-audio") chrome_options.add_argument("--disable-notifications") chrome_options.add_argument("--autoplay-policy=user-gesture-required") + # Essential WebGL arguments + chrome_options.add_argument("--ignore-gpu-blocklist") + chrome_options.add_argument("--enable-webgl") + chrome_options.add_argument("--enable-webgl-developer-extensions") + chrome_options.add_argument("--enable-webgl-draft-extensions") + chrome_options.add_argument("--disable-webgl-anti-fingerprinting") + chrome_options.add_argument("--allow-webgl-developer-extensions") + chrome_options.add_argument("--font-render-hinting=none") chrome_options.add_argument("--disable-blink-features=AutomationControlled") chrome_options.add_argument(f'user-agent={user_agent["ua"]}') resolutions = [(1920, 1080), (1366, 768), (1440, 900)] @@ -154,7 +165,7 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx stealth(driver, languages=["en-US", "en"], vendor=user_agent["vendor"], - platform="Win64" if "Windows" in user_agent["ua"] else "MacIntel" if "Macintosh" in user_agent["ua"] else "Linux x86_64", + platform="Win64" if "windows" in user_agent["ua"].lower() else "MacIntel" if "mac" in user_agent["ua"].lower() else "Linux x86_64", webgl_vendor="Intel Inc.", renderer="Intel Iris OpenGL Engine", fix_hairline=True, @@ -163,7 +174,14 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx security_prefs = { "profile.default_content_setting_values.media_stream": 2, "profile.default_content_setting_values.geolocation": 2, + "profile.default_content_setting_values.notifications": 2, + "profile.default_content_setting_values.camera": 2, + "profile.default_content_setting_values.microphone": 2, "safebrowsing.enabled": True, + "webkit.webprefs.accelerated_2d_canvas_enabled": True, + "webkit.webprefs.force_dark_mode_enabled": False, + "webkit.webprefs.accelerated_2d_canvas_enabled": True, + "webkit.webprefs.accelerated_2d_canvas_msaa_sample_count": 4, } chrome_options.add_experimental_option("prefs", security_prefs) chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"]) @@ -184,6 +202,7 @@ class Browser: except Exception as e: raise Exception(f"Failed to initialize browser: {str(e)}") self.setup_tabs() + self.patch_browser_fingerprint() if anticaptcha_manual_install: self.load_anticatpcha_manually() @@ -200,6 +219,30 @@ class Browser: pretty_print("You might want to install the AntiCaptcha extension for captchas.", color="warning") self.driver.get(self.anticaptcha) + def human_move(element): + actions = ActionChains(driver) + x_offset = random.randint(-5,5) + for _ in range(random.randint(2,5)): + actions.move_by_offset(x_offset, random.randint(-2,2)) + actions.pause(random.uniform(0.1,0.3)) + actions.click().perform() + + def human_scroll(self): + scroll_pixels = random.randint(200, 800) + scroll_time = random.uniform(0.5, 2.0) + self.driver.execute_script(f""" + window.scrollBy({{ + top: {scroll_pixels}, + behavior: 'smooth', + duration: {scroll_time} + }}); + """) + time.sleep(scroll_time + random.uniform(0.2, 0.5)) + + def patch_browser_fingerprint(self) -> None: + script = self.load_js("spoofing.js") + self.driver.execute_script(script) + def go_to(self, url:str) -> bool: """Navigate to a specified URL.""" time.sleep(random.uniform(0.4, 2.5)) # more human behavior @@ -216,7 +259,8 @@ class Browser: ) except TimeoutException: self.logger.warning("Timeout while waiting for page to bypass 'checking your browser'") - self.apply_web_safety() + #self.apply_web_safety() + self.human_scroll() self.logger.log(f"Navigated to: {url}") return True except TimeoutException as e: @@ -644,12 +688,17 @@ class Browser: input_elements = self.driver.execute_script(script) if __name__ == "__main__": - driver = create_driver(headless=False, stealth_mode=True) - browser = Browser(driver, anticaptcha_manual_install=True) + driver = create_driver(headless=False, stealth_mode=False) + browser = Browser(driver, anticaptcha_manual_install=False) input("press enter to continue") print("AntiCaptcha / Form Test") - #browser.go_to("https://www.browserscan.net/bot-detection") + browser.go_to("https://browserleaks.com/webgl") + time.sleep(20) + browser.go_to("https://bot.sannysoft.com/") + time.sleep(5) + browser.go_to("https://antoinevastel.com/bots/") + time.sleep(5) #txt = browser.get_text() #browser.go_to("https://www.google.com/recaptcha/api2/demo") browser.go_to("https://home.openweathermap.org/users/sign_up") @@ -658,3 +707,13 @@ if __name__ == "__main__": #inputs_fill = ['[q](checked)', '[q](checked)', '[user[username]](mlg)', '[user[email]](mlg.fcu@gmail.com)', '[user[password]](placeholder_P@ssw0rd123)', '[user[password_confirmation]](placeholder_P@ssw0rd123)'] #browser.fill_form(inputs_fill) input("press enter to exit") + +# Test sites for browser fingerprinting and captcha +""" +https://nowsecure.nl/ (thanks to user Michael Mintz) +https://bot.sannysoft.com +https://browserleaks.com/ +https://bot.incolumitas.com/ +https://fingerprintjs.github.io/fingerprintjs/ +https://antoinevastel.com/bots/ +""" \ No newline at end of file diff --git a/sources/web_scripts/inject_safety_script.js b/sources/web_scripts/inject_safety_script.js index 1ca2add..08fa14f 100644 --- a/sources/web_scripts/inject_safety_script.js +++ b/sources/web_scripts/inject_safety_script.js @@ -21,7 +21,5 @@ window.fetch = function() { console.log('Blocked fetch request'); return Promise.reject('Blocked'); }; -// Block annoying dialogs -window.alert = function() {}; -window.confirm = function() { return false; }; + window.prompt = function() { return null; }; \ No newline at end of file diff --git a/sources/web_scripts/spoofing.js b/sources/web_scripts/spoofing.js new file mode 100644 index 0000000..5d7ff89 --- /dev/null +++ b/sources/web_scripts/spoofing.js @@ -0,0 +1,194 @@ + +// Core automation masking +Object.defineProperty(navigator, 'webdriver', {get: () => undefined}); +delete window.cdc_adoQpoasnfa76pfcZLmcfl_Array; +delete window.cdc_adoQpoasnfa76pfcZLmcfl_Promise; +delete window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol; +Object.keys(window).forEach((key) => { + if (key.includes("webdriver") || key.includes("selenium") || key.includes("driver")) { + delete window[key]; + } +}); + +// Forceful chrome object spoofing +try { + Object.defineProperty(window, 'chrome', { + writable: true, + configurable: true, + value: { + app: {isInstalled: false}, + webstore: {onInstallStageChanged: {}, onDownloadProgress: {}}, + runtime: { + PlatformOs: {MAC: 'mac', WIN: 'win', ANDROID: 'android'}, + PlatformArch: {ARM: 'arm', X86_32: 'x86-32', X86_64: 'x86-64'}, + PlatformNaclArch: {ARM: 'arm', X86_32: 'x86-32', X86_64: 'x86-64'}, + RequestUpdateCheckStatus: { + THROTTLED: 'throttled', + NO_UPDATE: 'no_update', + UPDATE_AVAILABLE: 'update_available' + }, + OnInstalledReason: { + INSTALL: 'install', + UPDATE: 'update', + SHARED_MODULE_UPDATE: 'shared_module_update' + }, + OnRestartRequiredReason: { + APP_UPDATE: 'app_update', + OS_UPDATE: 'os_update', + PERIODIC: 'periodic' + } + } + } + }); +} catch (e) { + console.log("Error in defining window.chrome: ", e); + // Fallback: direct assignment + window.chrome = window.chrome || {}; + window.chrome.app = {isInstalled: false}; + window.chrome.webstore = {onInstallStageChanged: {}, onDownloadProgress: {}}; +} + +// Randomize plugins +const pluginsList = [ + {type: 'application/x-google-chrome-pdf', description: 'Portable Document Format', filename: 'internal-pdf-viewer', name: 'Chrome PDF Plugin'}, + {type: 'application/x-nacl', description: 'Native Client Executable', filename: 'internal-nacl-plugin', name: 'Native Client'}, + {type: 'application/x-ppapi-widevine-cdm', description: 'Widevine Content Decryption Module', filename: 'widevinecdm', name: 'Widevine CDM'} +]; +Object.defineProperty(navigator, 'plugins', { + get: () => pluginsList.slice(0, Math.floor(Math.random() * pluginsList.length) + 1) +}); + +// Font spoofing with randomized font list +const fontList = ['Arial', 'Helvetica', 'Times New Roman', 'Courier New', 'Verdana']; +Object.defineProperty(document, 'fonts', { + value: { + add: function() {}, + check: function(font) { return fontList.includes(font.split(' ').slice(-1)[0]); }, + delete: function() {}, + forEach: function(cb) { fontList.forEach(f => cb(f)); }, + has: function(font) { return fontList.includes(font.split(' ').slice(-1)[0]); }, + keys: function() { return fontList; }, + size: fontList.length + } +}); + +// Canvas fingerprint spoofing +const originalToDataURL = HTMLCanvasElement.prototype.toDataURL; +HTMLCanvasElement.prototype.toDataURL = function() { + const ctx = this.getContext('2d'); + ctx.fillStyle = `rgba(${Math.random() * 10},${Math.random() * 10},${Math.random() * 10},0.01)`; + ctx.fillRect(0, 0, 1, 1); + return originalToDataURL.apply(this, arguments); +}; + +// Screen resolution spoofing +const resolutions = [[1920, 1080], [1366, 768], [1440, 900]]; +const [w, h] = resolutions[Math.floor(Math.random() * resolutions.length)]; +Object.defineProperty(window, 'screen', { + value: { + width: w, + height: h, + availWidth: w, + availHeight: h - 50, + colorDepth: 24, + pixelDepth: 24 + } +}); + +// Timezone and language spoofing +Object.defineProperty(navigator, 'language', {get: () => 'en-US'}); +Intl.DateTimeFormat = function() { + return {resolvedOptions: () => ({timeZone: 'America/New_York'})}; +}; + +// AudioContext spoofing +const originalCreate = window.AudioContext || window.webkitAudioContext; +window.AudioContext = window.webkitAudioContext = function() { + const context = new originalCreate(); + const dest = context.createAnalyser(); + dest.fake = true; + return context; +}; + +// WebRTC spoofing +Object.defineProperty(navigator, 'mediaDevices', { + get: () => undefined +}); +Object.defineProperty(navigator, 'getUserMedia', { + get: () => undefined +}); + +// web gl spoofing +const getParameter = WebGLRenderingContext.prototype.getParameter; +WebGLRenderingContext.prototype.getParameter = function(parameter) { + // Common WebGL parameters + const params = { + 37445: 'Intel Open Source Technology Center', // VENDOR + 37446: 'Mesa DRI IntelĀ® HD Graphics 4000', // RENDERER + 34076: 'WebKit WebGL', // SHADING_LANGUAGE_VERSION + 35661: '2.1 INTEL-16.4.5', // VERSION + 36349: 'Intel Inc.' // UNMASKED_VENDOR_WEBGL + }; + return params[parameter] || getParameter.call(this, parameter); +}; + +// Performance API spoofing +if ('performance' in window) { + Object.defineProperty(performance, 'memory', { + value: { + jsHeapSizeLimit: 4294705152, + totalJSHeapSize: 78365432, + usedJSHeapSize: 46543210 + }, + configurable: true + }); + } + + // Battery API spoofing +if ('getBattery' in navigator) { + Object.defineProperty(navigator, 'getBattery', { + value: () => Promise.resolve({ + level: 0.85, + charging: true, + chargingTime: 1800, + dischargingTime: Infinity, + onchargingchange: null, + onchargingtimechange: null, + ondischargingtimechange: null, + onlevelchange: null + }), + configurable: true + }); + } + +window.RTCPeerConnection = undefined; +window.webkitRTCPeerConnection = undefined; +window.mozRTCPeerConnection = undefined; + +Object.defineProperty(navigator, 'permissions', { + value: { + query: () => Promise.resolve({ state: 'denied' }) + } +}); +Object.defineProperty(navigator, 'languages', { + get: () => ['en-US', 'en'], +}); + +try { + const canvas = document.createElement('canvas'); + const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); + if (gl) { + const debugInfo = gl.getExtension('WEBGL_debug_renderer_info'); + Object.defineProperty(WebGLRenderingContext.prototype, 'getParameter', { + value: function(parameter) { + if (parameter === 37445) { // UNMASKED_VENDOR_WEBGL + return 'Intel Inc.'; + } + if (parameter === 37446) { // UNMASKED_RENDERER_WEBGL + return 'Intel Iris OpenGL Engine'; + } + return this.__proto__.getParameter(parameter); + } + }); + } +} catch(e) {} \ No newline at end of file From 637ca0f826ea2f198335a639e17e080151815c02 Mon Sep 17 00:00:00 2001 From: martin legrand Date: Wed, 14 May 2025 21:55:59 +0200 Subject: [PATCH 03/10] feat : attempt to bypass bot detection even more --- sources/browser.py | 27 +++++++++----- sources/web_scripts/spoofing.js | 65 ++++++++++++++++++++++++--------- 2 files changed, 65 insertions(+), 27 deletions(-) diff --git a/sources/browser.py b/sources/browser.py index 2bb7b92..30415b0 100644 --- a/sources/browser.py +++ b/sources/browser.py @@ -172,16 +172,25 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx ) return driver security_prefs = { - "profile.default_content_setting_values.media_stream": 2, - "profile.default_content_setting_values.geolocation": 2, - "profile.default_content_setting_values.notifications": 2, - "profile.default_content_setting_values.camera": 2, - "profile.default_content_setting_values.microphone": 2, + # Set all permissions to prompt (1=allow, 2=block, 0=prompt) + "profile.default_content_setting_values.geolocation": 0, + "profile.default_content_setting_values.notifications": 0, + "profile.default_content_setting_values.camera": 0, + "profile.default_content_setting_values.microphone": 0, + "profile.default_content_setting_values.midi_sysex": 0, + "profile.default_content_setting_values.clipboard": 0, + "profile.default_content_setting_values.media_stream": 0, + "profile.default_content_setting_values.background_sync": 0, + "profile.default_content_setting_values.sensors": 0, + "profile.default_content_setting_values.accessibility_events": 0, "safebrowsing.enabled": True, + "credentials_enable_service": False, + "profile.password_manager_enabled": False, "webkit.webprefs.accelerated_2d_canvas_enabled": True, "webkit.webprefs.force_dark_mode_enabled": False, - "webkit.webprefs.accelerated_2d_canvas_enabled": True, "webkit.webprefs.accelerated_2d_canvas_msaa_sample_count": 4, + "enable_webgl": True, + "enable_webgl2_compute_context": True } chrome_options.add_experimental_option("prefs", security_prefs) chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"]) @@ -688,15 +697,13 @@ class Browser: input_elements = self.driver.execute_script(script) if __name__ == "__main__": - driver = create_driver(headless=False, stealth_mode=False) + driver = create_driver(headless=False, stealth_mode=True) browser = Browser(driver, anticaptcha_manual_install=False) input("press enter to continue") print("AntiCaptcha / Form Test") - browser.go_to("https://browserleaks.com/webgl") - time.sleep(20) browser.go_to("https://bot.sannysoft.com/") - time.sleep(5) + time.sleep(50) browser.go_to("https://antoinevastel.com/bots/") time.sleep(5) #txt = browser.get_text() diff --git a/sources/web_scripts/spoofing.js b/sources/web_scripts/spoofing.js index 5d7ff89..96faf2b 100644 --- a/sources/web_scripts/spoofing.js +++ b/sources/web_scripts/spoofing.js @@ -4,6 +4,7 @@ Object.defineProperty(navigator, 'webdriver', {get: () => undefined}); delete window.cdc_adoQpoasnfa76pfcZLmcfl_Array; delete window.cdc_adoQpoasnfa76pfcZLmcfl_Promise; delete window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol; + Object.keys(window).forEach((key) => { if (key.includes("webdriver") || key.includes("selenium") || key.includes("driver")) { delete window[key]; @@ -174,21 +175,51 @@ Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'], }); -try { - const canvas = document.createElement('canvas'); - const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); - if (gl) { - const debugInfo = gl.getExtension('WEBGL_debug_renderer_info'); - Object.defineProperty(WebGLRenderingContext.prototype, 'getParameter', { - value: function(parameter) { - if (parameter === 37445) { // UNMASKED_VENDOR_WEBGL - return 'Intel Inc.'; - } - if (parameter === 37446) { // UNMASKED_RENDERER_WEBGL - return 'Intel Iris OpenGL Engine'; - } - return this.__proto__.getParameter(parameter); +const canvas = document.createElement('canvas'); +const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); + +if (gl) { + // Store original getParameter function + const originalGetParameter = gl.getParameter; + // Override getParameter to spoof values + gl.getParameter = function(parameter) { + // UNMASKED_VENDOR_WEBGL + if (parameter === 37445) return 'Intel Inc.'; + // UNMASKED_RENDERER_WEBGL + if (parameter === 37446) return 'Intel Iris Pro OpenGL Engine'; + // Other common fingerprint points + if (parameter === gl.VERSION) return 'WebGL 2.0 (OpenGL ES 3.0 Intel)'; + if (parameter === gl.SHADING_LANGUAGE_VERSION) return 'WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0)'; + + // Default to original behavior + return originalGetParameter.call(gl, parameter); + }; + + // Spoof debug renderer info extension + const debugInfo = { + getExtension: function(name) { + if (name === 'WEBGL_debug_renderer_info') { + return { + UNMASKED_VENDOR_WEBGL: 37445, + UNMASKED_RENDERER_WEBGL: 37446, + __proto__: WebGLExtension.prototype + }; } - }); - } -} catch(e) {} \ No newline at end of file + return null; + }, + __proto__: WebGLRenderingContext.prototype + }; + + // Override getExtension + const originalGetExtension = gl.getExtension; + gl.getExtension = function(name) { + if (name === 'WEBGL_debug_renderer_info') { + return debugInfo; + } + return originalGetExtension.call(gl, name); + }; + + // Freeze objects to prevent detection + Object.freeze(gl); + Object.freeze(debugInfo); +} \ No newline at end of file From 38b1e176281e884beaf6118c9b5c2f5fdefc2e11 Mon Sep 17 00:00:00 2001 From: martin legrand Date: Thu, 15 May 2025 15:13:25 +0200 Subject: [PATCH 04/10] feat : better browser spoofing --- sources/browser.py | 36 +++-- sources/web_scripts/spoofing.js | 231 +++++++++----------------------- 2 files changed, 83 insertions(+), 184 deletions(-) diff --git a/sources/browser.py b/sources/browser.py index 30415b0..1f3bd9d 100644 --- a/sources/browser.py +++ b/sources/browser.py @@ -127,21 +127,22 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx user_data_dir = tempfile.mkdtemp() user_agent = get_random_user_agent() chrome_options.add_argument(f"--user-data-dir={user_data_dir}") + chrome_options.add_argument("accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8") + chrome_options.add_argument("--accept-lang=fr-FR,fr;q=0.9") + chrome_options.add_argument("--timezone=Europe/Paris") chrome_options.add_argument("--use-gl=egl") - chrome_options.add_argument("--enable-webgl") + chrome_options.add_argument("--enable-webgl") # no webgl is red flag for some anti-bot chrome_options.add_argument("--enable-3d-apis") + chrome_options.add_argument("--use-gpu-driver-bug-workaround") + chrome_options.add_argument("--allow-webgl-developer-extensions") chrome_options.add_argument("--no-sandbox") chrome_options.add_argument("--disable-dev-shm-usage") chrome_options.add_argument("--mute-audio") chrome_options.add_argument("--disable-notifications") chrome_options.add_argument("--autoplay-policy=user-gesture-required") + chrome_options.add_argument("--disable-features=SitePerProcess,IsolateOrigins") + chrome_options.add_argument("--enable-features=NetworkService,NetworkServiceInProcess") # Essential WebGL arguments - chrome_options.add_argument("--ignore-gpu-blocklist") - chrome_options.add_argument("--enable-webgl") - chrome_options.add_argument("--enable-webgl-developer-extensions") - chrome_options.add_argument("--enable-webgl-draft-extensions") - chrome_options.add_argument("--disable-webgl-anti-fingerprinting") - chrome_options.add_argument("--allow-webgl-developer-extensions") chrome_options.add_argument("--font-render-hinting=none") chrome_options.add_argument("--disable-blink-features=AutomationControlled") chrome_options.add_argument(f'user-agent={user_agent["ua"]}') @@ -237,16 +238,13 @@ class Browser: actions.click().perform() def human_scroll(self): - scroll_pixels = random.randint(200, 800) - scroll_time = random.uniform(0.5, 2.0) - self.driver.execute_script(f""" - window.scrollBy({{ - top: {scroll_pixels}, - behavior: 'smooth', - duration: {scroll_time} - }}); - """) - time.sleep(scroll_time + random.uniform(0.2, 0.5)) + for _ in range(random.randint(1, 3)): + scroll_pixels = random.randint(150, 1200) + self.driver.execute_script(f"window.scrollBy(0, {scroll_pixels});") + time.sleep(random.uniform(0.5, 2.0)) + if random.random() < 0.4: + self.driver.execute_script(f"window.scrollBy(0, -{random.randint(50, 300)});") + time.sleep(random.uniform(0.3, 1.0)) def patch_browser_fingerprint(self) -> None: script = self.load_js("spoofing.js") @@ -258,6 +256,7 @@ class Browser: try: initial_handles = self.driver.window_handles self.driver.get(url) + time.sleep(random.uniform(0.01, 0.3)) try: wait = WebDriverWait(self.driver, timeout=10) wait.until( @@ -269,6 +268,7 @@ class Browser: except TimeoutException: self.logger.warning("Timeout while waiting for page to bypass 'checking your browser'") #self.apply_web_safety() + time.sleep(random.uniform(0.01, 0.2)) self.human_scroll() self.logger.log(f"Navigated to: {url}") return True @@ -702,8 +702,6 @@ if __name__ == "__main__": input("press enter to continue") print("AntiCaptcha / Form Test") - browser.go_to("https://bot.sannysoft.com/") - time.sleep(50) browser.go_to("https://antoinevastel.com/bots/") time.sleep(5) #txt = browser.get_text() diff --git a/sources/web_scripts/spoofing.js b/sources/web_scripts/spoofing.js index 96faf2b..f7c6a67 100644 --- a/sources/web_scripts/spoofing.js +++ b/sources/web_scripts/spoofing.js @@ -1,55 +1,35 @@ // Core automation masking -Object.defineProperty(navigator, 'webdriver', {get: () => undefined}); delete window.cdc_adoQpoasnfa76pfcZLmcfl_Array; delete window.cdc_adoQpoasnfa76pfcZLmcfl_Promise; delete window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol; +window.RTCPeerConnection = undefined; +window.webkitRTCPeerConnection = undefined; +window.mozRTCPeerConnection = undefined; + +window.Notification = class Notification { + constructor(title, options = {}) { + this.title = title; + this.options = options; + } + static permission = 'granted'; + static requestPermission = () => Promise.resolve('granted'); + close() {} + onclick = null; + onerror = null; + onclose = null; + onshow = null; +}; + Object.keys(window).forEach((key) => { if (key.includes("webdriver") || key.includes("selenium") || key.includes("driver")) { delete window[key]; } }); -// Forceful chrome object spoofing -try { - Object.defineProperty(window, 'chrome', { - writable: true, - configurable: true, - value: { - app: {isInstalled: false}, - webstore: {onInstallStageChanged: {}, onDownloadProgress: {}}, - runtime: { - PlatformOs: {MAC: 'mac', WIN: 'win', ANDROID: 'android'}, - PlatformArch: {ARM: 'arm', X86_32: 'x86-32', X86_64: 'x86-64'}, - PlatformNaclArch: {ARM: 'arm', X86_32: 'x86-32', X86_64: 'x86-64'}, - RequestUpdateCheckStatus: { - THROTTLED: 'throttled', - NO_UPDATE: 'no_update', - UPDATE_AVAILABLE: 'update_available' - }, - OnInstalledReason: { - INSTALL: 'install', - UPDATE: 'update', - SHARED_MODULE_UPDATE: 'shared_module_update' - }, - OnRestartRequiredReason: { - APP_UPDATE: 'app_update', - OS_UPDATE: 'os_update', - PERIODIC: 'periodic' - } - } - } - }); -} catch (e) { - console.log("Error in defining window.chrome: ", e); - // Fallback: direct assignment - window.chrome = window.chrome || {}; - window.chrome.app = {isInstalled: false}; - window.chrome.webstore = {onInstallStageChanged: {}, onDownloadProgress: {}}; -} - // Randomize plugins + const pluginsList = [ {type: 'application/x-google-chrome-pdf', description: 'Portable Document Format', filename: 'internal-pdf-viewer', name: 'Chrome PDF Plugin'}, {type: 'application/x-nacl', description: 'Native Client Executable', filename: 'internal-nacl-plugin', name: 'Native Client'}, @@ -59,7 +39,8 @@ Object.defineProperty(navigator, 'plugins', { get: () => pluginsList.slice(0, Math.floor(Math.random() * pluginsList.length) + 1) }); -// Font spoofing with randomized font list +// Font spoofing + const fontList = ['Arial', 'Helvetica', 'Times New Roman', 'Courier New', 'Verdana']; Object.defineProperty(document, 'fonts', { value: { @@ -74,64 +55,53 @@ Object.defineProperty(document, 'fonts', { }); // Canvas fingerprint spoofing -const originalToDataURL = HTMLCanvasElement.prototype.toDataURL; + HTMLCanvasElement.prototype.toDataURL = function() { const ctx = this.getContext('2d'); - ctx.fillStyle = `rgba(${Math.random() * 10},${Math.random() * 10},${Math.random() * 10},0.01)`; - ctx.fillRect(0, 0, 1, 1); + // Add varied noise to avoid consistent fingerprints + for (let i = 0; i < 10; i++) { + ctx.fillStyle = `rgba(${Math.random() * 5}, ${Math.random() * 5}, ${Math.random() * 5}, 0.005)`; + ctx.fillRect(Math.random() * this.width, Math.random() * this.height, 1, 1); + } return originalToDataURL.apply(this, arguments); }; -// Screen resolution spoofing -const resolutions = [[1920, 1080], [1366, 768], [1440, 900]]; -const [w, h] = resolutions[Math.floor(Math.random() * resolutions.length)]; +const [w, h] = [1920, 1080]; Object.defineProperty(window, 'screen', { - value: { - width: w, - height: h, - availWidth: w, - availHeight: h - 50, - colorDepth: 24, - pixelDepth: 24 - } + value: { + width: w, + height: h, + availWidth: w - 20, + availHeight: h - 100, + colorDepth: 24, + pixelDepth: 24 + } }); -// Timezone and language spoofing -Object.defineProperty(navigator, 'language', {get: () => 'en-US'}); -Intl.DateTimeFormat = function() { - return {resolvedOptions: () => ({timeZone: 'America/New_York'})}; + +// ===== WebGL Consistency ===== +const os = navigator.userAgent.includes('Windows') ? 'Windows' : 'Mac'; +const webGLParams = { + 'Windows': { + 37445: 'Google Inc. (NVIDIA)', // VENDOR + 37446: 'ANGLE (NVIDIA, NVIDIA GeForce RTX 3060)', // RENDERER + 36349: 'NVIDIA Corporation', // UNMASKED_VENDOR_WEBGL + 37444: 'NVIDIA GeForce RTX 3060', // UNMASKED_RENDERER_WEBGL + 35661: 'WebGL 2.0' // VERSION + }, + 'Mac': { + 37445: 'Apple Inc.', + 37446: 'Apple M1 Pro', + 36349: 'Apple', + 37444: 'Apple M1 Pro', + 35661: 'WebGL 2.0 (Metal)' + } }; -// AudioContext spoofing -const originalCreate = window.AudioContext || window.webkitAudioContext; -window.AudioContext = window.webkitAudioContext = function() { - const context = new originalCreate(); - const dest = context.createAnalyser(); - dest.fake = true; - return context; -}; - -// WebRTC spoofing -Object.defineProperty(navigator, 'mediaDevices', { - get: () => undefined -}); -Object.defineProperty(navigator, 'getUserMedia', { - get: () => undefined -}); - -// web gl spoofing -const getParameter = WebGLRenderingContext.prototype.getParameter; +// replace WebGL parameters WebGLRenderingContext.prototype.getParameter = function(parameter) { - // Common WebGL parameters - const params = { - 37445: 'Intel Open Source Technology Center', // VENDOR - 37446: 'Mesa DRI IntelĀ® HD Graphics 4000', // RENDERER - 34076: 'WebKit WebGL', // SHADING_LANGUAGE_VERSION - 35661: '2.1 INTEL-16.4.5', // VERSION - 36349: 'Intel Inc.' // UNMASKED_VENDOR_WEBGL - }; - return params[parameter] || getParameter.call(this, parameter); -}; + return webGLParams[os][parameter] || getParameter.call(this, parameter); + }; // Performance API spoofing if ('performance' in window) { @@ -145,81 +115,12 @@ if ('performance' in window) { }); } - // Battery API spoofing -if ('getBattery' in navigator) { - Object.defineProperty(navigator, 'getBattery', { - value: () => Promise.resolve({ - level: 0.85, - charging: true, - chargingTime: 1800, - dischargingTime: Infinity, - onchargingchange: null, - onchargingtimechange: null, - ondischargingtimechange: null, - onlevelchange: null - }), - configurable: true - }); - } - -window.RTCPeerConnection = undefined; -window.webkitRTCPeerConnection = undefined; -window.mozRTCPeerConnection = undefined; - -Object.defineProperty(navigator, 'permissions', { - value: { - query: () => Promise.resolve({ state: 'denied' }) - } -}); -Object.defineProperty(navigator, 'languages', { - get: () => ['en-US', 'en'], -}); - -const canvas = document.createElement('canvas'); -const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); - -if (gl) { - // Store original getParameter function - const originalGetParameter = gl.getParameter; - // Override getParameter to spoof values - gl.getParameter = function(parameter) { - // UNMASKED_VENDOR_WEBGL - if (parameter === 37445) return 'Intel Inc.'; - // UNMASKED_RENDERER_WEBGL - if (parameter === 37446) return 'Intel Iris Pro OpenGL Engine'; - // Other common fingerprint points - if (parameter === gl.VERSION) return 'WebGL 2.0 (OpenGL ES 3.0 Intel)'; - if (parameter === gl.SHADING_LANGUAGE_VERSION) return 'WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0)'; - - // Default to original behavior - return originalGetParameter.call(gl, parameter); - }; - - // Spoof debug renderer info extension - const debugInfo = { - getExtension: function(name) { - if (name === 'WEBGL_debug_renderer_info') { - return { - UNMASKED_VENDOR_WEBGL: 37445, - UNMASKED_RENDERER_WEBGL: 37446, - __proto__: WebGLExtension.prototype - }; - } - return null; - }, - __proto__: WebGLRenderingContext.prototype - }; - - // Override getExtension - const originalGetExtension = gl.getExtension; - gl.getExtension = function(name) { - if (name === 'WEBGL_debug_renderer_info') { - return debugInfo; - } - return originalGetExtension.call(gl, name); - }; - - // Freeze objects to prevent detection - Object.freeze(gl); - Object.freeze(debugInfo); -} \ No newline at end of file +const originalCreate = window.AudioContext || window.webkitAudioContext; +window.AudioContext = window.webkitAudioContext = function() { + const context = new originalCreate(); + const analyser = context.createAnalyser(); + analyser.fake = true; // Mark as spoofed + // Spoof common methods + analyser.getFloatFrequencyData = () => new Float32Array(1024).fill(Math.random() * -100); + return context; +}; \ No newline at end of file From 95d5aea1d577fb6d687cae2cd77503a168bfb2bc Mon Sep 17 00:00:00 2001 From: martin legrand Date: Fri, 16 May 2025 09:04:19 +0200 Subject: [PATCH 05/10] feat : upgrade stealthness --- sources/browser.py | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/sources/browser.py b/sources/browser.py index 1f3bd9d..da6ebfa 100644 --- a/sources/browser.py +++ b/sources/browser.py @@ -110,7 +110,7 @@ def create_undetected_chromedriver(service, chrome_options) -> webdriver.Chrome: driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})") return driver -def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx") -> webdriver.Chrome: +def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx", lang="en") -> webdriver.Chrome: """Create a Chrome WebDriver with specified options.""" chrome_options = Options() chrome_path = get_chrome_path() @@ -121,20 +121,14 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx if headless: chrome_options.add_argument("--headless") - chrome_options.add_argument("--use-gl=swiftshader") - #chrome_options.add_argument("--disable-gpu") - #chrome_options.add_argument("--disable-webgl") # prevent some website from working, commented for now + chrome_options.add_argument("--disable-gpu") + chrome_options.add_argument("--disable-webgl") # prevent some website from working, commented for now user_data_dir = tempfile.mkdtemp() user_agent = get_random_user_agent() + width, height = (1920, 1080) chrome_options.add_argument(f"--user-data-dir={user_data_dir}") - chrome_options.add_argument("accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8") - chrome_options.add_argument("--accept-lang=fr-FR,fr;q=0.9") + chrome_options.add_argument(f"--accept-lang={lang}-{lang.upper()},{lang};q=0.9") chrome_options.add_argument("--timezone=Europe/Paris") - chrome_options.add_argument("--use-gl=egl") - chrome_options.add_argument("--enable-webgl") # no webgl is red flag for some anti-bot - chrome_options.add_argument("--enable-3d-apis") - chrome_options.add_argument("--use-gpu-driver-bug-workaround") - chrome_options.add_argument("--allow-webgl-developer-extensions") chrome_options.add_argument("--no-sandbox") chrome_options.add_argument("--disable-dev-shm-usage") chrome_options.add_argument("--mute-audio") @@ -142,12 +136,8 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx chrome_options.add_argument("--autoplay-policy=user-gesture-required") chrome_options.add_argument("--disable-features=SitePerProcess,IsolateOrigins") chrome_options.add_argument("--enable-features=NetworkService,NetworkServiceInProcess") - # Essential WebGL arguments - chrome_options.add_argument("--font-render-hinting=none") chrome_options.add_argument("--disable-blink-features=AutomationControlled") chrome_options.add_argument(f'user-agent={user_agent["ua"]}') - resolutions = [(1920, 1080), (1366, 768), (1440, 900)] - width, height = random.choice(resolutions) chrome_options.add_argument(f'--window-size={width},{height}') if not stealth_mode: # crx file can't be installed in stealth mode @@ -702,7 +692,7 @@ if __name__ == "__main__": input("press enter to continue") print("AntiCaptcha / Form Test") - browser.go_to("https://antoinevastel.com/bots/") + browser.go_to("https://bot.sannysoft.com") time.sleep(5) #txt = browser.get_text() #browser.go_to("https://www.google.com/recaptcha/api2/demo") @@ -715,7 +705,7 @@ if __name__ == "__main__": # Test sites for browser fingerprinting and captcha """ -https://nowsecure.nl/ (thanks to user Michael Mintz) +https://nowsecure.nl/ https://bot.sannysoft.com https://browserleaks.com/ https://bot.incolumitas.com/ From 384d9a8c0b3909a695877ad7c34ab383f5f69920 Mon Sep 17 00:00:00 2001 From: martin legrand Date: Fri, 16 May 2025 09:19:45 +0200 Subject: [PATCH 06/10] feat : markdown support on frontend --- frontend/agentic-seek-front/package-lock.json | 1177 +++++++++++++++++ frontend/agentic-seek-front/package.json | 1 + frontend/agentic-seek-front/src/App.js | 3 +- 3 files changed, 1180 insertions(+), 1 deletion(-) diff --git a/frontend/agentic-seek-front/package-lock.json b/frontend/agentic-seek-front/package-lock.json index ce7cc3a..21909dd 100644 --- a/frontend/agentic-seek-front/package-lock.json +++ b/frontend/agentic-seek-front/package-lock.json @@ -15,6 +15,7 @@ "axios": "^1.8.4", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-markdown": "^10.1.0", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" } @@ -3652,6 +3653,15 @@ "@types/node": "*" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/eslint": { "version": "8.56.12", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", @@ -3678,6 +3688,15 @@ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "license": "MIT" }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/@types/express": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", @@ -3723,6 +3742,15 @@ "@types/node": "*" } }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -3780,12 +3808,27 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "license": "MIT" }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "license": "MIT" }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.14.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", @@ -3834,6 +3877,16 @@ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "license": "MIT" }, + "node_modules/@types/react": { + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.4.tgz", + "integrity": "sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g==", + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.0.2" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -3906,6 +3959,12 @@ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "license": "MIT" }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -5187,6 +5246,16 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -5533,6 +5602,16 @@ "node": ">=4" } }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5558,6 +5637,46 @@ "node": ">=10" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/check-types": { "version": "11.2.3", "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz", @@ -5805,6 +5924,16 @@ "node": ">= 0.8" } }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", @@ -6392,6 +6521,13 @@ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", "license": "MIT" }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT", + "peer": true + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -6486,6 +6622,19 @@ "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", "license": "MIT" }, + "node_modules/decode-named-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.1.0.tgz", + "integrity": "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -6646,6 +6795,19 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -7797,6 +7959,16 @@ "node": ">=4.0" } }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/estree-walker": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", @@ -7943,6 +8115,12 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -8862,6 +9040,46 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -8983,6 +9201,16 @@ "node": ">=12" } }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/html-webpack-plugin": { "version": "5.6.3", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz", @@ -9282,6 +9510,12 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC" }, + "node_modules/inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", + "license": "MIT" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -9305,6 +9539,30 @@ "node": ">= 10" } }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -9450,6 +9708,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -9537,6 +9805,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -11217,6 +11495,16 @@ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", "license": "MIT" }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -11307,6 +11595,159 @@ "node": ">= 0.4" } }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", @@ -11367,6 +11808,448 @@ "node": ">= 0.6" } }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -11989,6 +12872,31 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -13634,6 +14542,16 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -13950,6 +14868,33 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "license": "MIT" }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -14229,6 +15174,39 @@ "node": ">= 0.10" } }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", @@ -15134,6 +16112,16 @@ "deprecated": "Please use @jridgewell/sourcemap-codec instead", "license": "MIT" }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/spdy": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", @@ -15489,6 +16477,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/stringify-object": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", @@ -15595,6 +16597,24 @@ "webpack": "^5.0.0" } }, + "node_modules/style-to-js": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.16.tgz", + "integrity": "sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.8" + } + }, + "node_modules/style-to-object": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", + "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.4" + } + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -16180,6 +17200,26 @@ "node": ">=8" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", @@ -16465,6 +17505,37 @@ "node": ">=4" } }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -16477,6 +17548,74 @@ "node": ">=8" } }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -16634,6 +17773,34 @@ "node": ">= 0.8" } }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", @@ -17570,6 +18737,16 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/frontend/agentic-seek-front/package.json b/frontend/agentic-seek-front/package.json index db60f97..21a8717 100644 --- a/frontend/agentic-seek-front/package.json +++ b/frontend/agentic-seek-front/package.json @@ -10,6 +10,7 @@ "axios": "^1.8.4", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-markdown": "^10.1.0", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" }, diff --git a/frontend/agentic-seek-front/src/App.js b/frontend/agentic-seek-front/src/App.js index c4350ea..7da1a53 100644 --- a/frontend/agentic-seek-front/src/App.js +++ b/frontend/agentic-seek-front/src/App.js @@ -1,4 +1,5 @@ import React, { useState, useEffect, useRef } from 'react'; +import ReactMarkdown from 'react-markdown'; import axios from 'axios'; import './App.css'; import { colors } from './colors'; @@ -193,7 +194,7 @@ function App() { {msg.type === 'agent' && ( {msg.agentName} )} -

{msg.content}

+ {msg.content} )) )} From 45fbf5a88cc0b8d75f9e94b3ecfba105446c9b38 Mon Sep 17 00:00:00 2001 From: martin legrand Date: Fri, 16 May 2025 09:24:46 +0200 Subject: [PATCH 07/10] fix comment --- sources/browser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/browser.py b/sources/browser.py index da6ebfa..36fafdd 100644 --- a/sources/browser.py +++ b/sources/browser.py @@ -122,7 +122,7 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx if headless: chrome_options.add_argument("--headless") chrome_options.add_argument("--disable-gpu") - chrome_options.add_argument("--disable-webgl") # prevent some website from working, commented for now + chrome_options.add_argument("--disable-webgl") user_data_dir = tempfile.mkdtemp() user_agent = get_random_user_agent() width, height = (1920, 1080) From bd26d7233d95389a6b3dbcdff6e6132325cc2ae7 Mon Sep 17 00:00:00 2001 From: martin legrand Date: Fri, 16 May 2025 10:18:26 +0200 Subject: [PATCH 08/10] refactor : comments + feat : selected lang in config influe browser config --- api.py | 2 +- cli.py | 2 +- sources/browser.py | 24 ++++++++++-------------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/api.py b/api.py index 5217280..194a29d 100755 --- a/api.py +++ b/api.py @@ -58,7 +58,7 @@ def initialize_system(): logger.info(f"Provider initialized: {provider.provider_name} ({provider.model})") browser = Browser( - create_driver(headless=config.getboolean('BROWSER', 'headless_browser'), stealth_mode=stealth_mode), + create_driver(headless=config.getboolean('BROWSER', 'headless_browser'), stealth_mode=stealth_mode, lang=languages[0]), anticaptcha_manual_install=stealth_mode ) logger.info("Browser initialized") diff --git a/cli.py b/cli.py index 59cb98f..d69bc64 100755 --- a/cli.py +++ b/cli.py @@ -29,7 +29,7 @@ async def main(): is_local=config.getboolean('MAIN', 'is_local')) browser = Browser( - create_driver(headless=config.getboolean('BROWSER', 'headless_browser'), stealth_mode=stealth_mode), + create_driver(headless=config.getboolean('BROWSER', 'headless_browser'), stealth_mode=stealth_mode, lang=languages[0]), anticaptcha_manual_install=stealth_mode ) diff --git a/sources/browser.py b/sources/browser.py index 36fafdd..e33f1df 100644 --- a/sources/browser.py +++ b/sources/browser.py @@ -45,7 +45,7 @@ def get_chrome_path() -> str: paths = ["/usr/bin/google-chrome", "/usr/bin/chromium-browser", "/usr/bin/chromium", "/opt/chrome/chrome", "/usr/local/bin/chrome"] for path in paths: - if os.path.exists(path) and os.access(path, os.X_OK): # Check if executable + if os.path.exists(path) and os.access(path, os.X_OK): return path print("Looking for Google Chrome in these locations failed:") print('\n'.join(paths)) @@ -140,7 +140,6 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx chrome_options.add_argument(f'user-agent={user_agent["ua"]}') chrome_options.add_argument(f'--window-size={width},{height}') if not stealth_mode: - # crx file can't be installed in stealth mode if not os.path.exists(crx_path): pretty_print(f"Anti-captcha CRX not found at {crx_path}.", color="failure") else: @@ -163,7 +162,6 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx ) return driver security_prefs = { - # Set all permissions to prompt (1=allow, 2=block, 0=prompt) "profile.default_content_setting_values.geolocation": 0, "profile.default_content_setting_values.notifications": 0, "profile.default_content_setting_values.camera": 0, @@ -242,7 +240,7 @@ class Browser: def go_to(self, url:str) -> bool: """Navigate to a specified URL.""" - time.sleep(random.uniform(0.4, 2.5)) # more human behavior + time.sleep(random.uniform(0.4, 2.5)) try: initial_handles = self.driver.window_handles self.driver.get(url) @@ -257,7 +255,7 @@ class Browser: ) except TimeoutException: self.logger.warning("Timeout while waiting for page to bypass 'checking your browser'") - #self.apply_web_safety() + self.apply_web_safety() time.sleep(random.uniform(0.01, 0.2)) self.human_scroll() self.logger.log(f"Navigated to: {url}") @@ -665,7 +663,7 @@ class Browser: try: original_zoom = self.driver.execute_script("return document.body.style.zoom || 1;") self.driver.execute_script("document.body.style.zoom='75%'") - time.sleep(0.1) # Allow time for the zoom to take effect + time.sleep(0.1) path = os.path.join(self.screenshot_folder, filename) if not os.path.exists(self.screenshot_folder): os.makedirs(self.screenshot_folder) @@ -704,11 +702,9 @@ if __name__ == "__main__": input("press enter to exit") # Test sites for browser fingerprinting and captcha -""" -https://nowsecure.nl/ -https://bot.sannysoft.com -https://browserleaks.com/ -https://bot.incolumitas.com/ -https://fingerprintjs.github.io/fingerprintjs/ -https://antoinevastel.com/bots/ -""" \ No newline at end of file +# https://nowsecure.nl/ +# https://bot.sannysoft.com +# https://browserleaks.com/ +# https://bot.incolumitas.com/ +# https://fingerprintjs.github.io/fingerprintjs/ +# https://antoinevastel.com/bots/ \ No newline at end of file From 50a9cb8d278a0fa27e31f46f886b4f16c3b6c347 Mon Sep 17 00:00:00 2001 From: martin legrand Date: Fri, 16 May 2025 12:41:56 +0200 Subject: [PATCH 09/10] fix : frontend color --- frontend/agentic-seek-front/src/App.css | 4 ++++ frontend/agentic-seek-front/src/App.js | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/frontend/agentic-seek-front/src/App.css b/frontend/agentic-seek-front/src/App.css index 1c051ab..d9ac01c 100644 --- a/frontend/agentic-seek-front/src/App.css +++ b/frontend/agentic-seek-front/src/App.css @@ -366,6 +366,10 @@ body { color: #28a745; /* success */ } +.block-failure { + color: #d21b0b; /* success */ +} + .block pre { background-color: #1a202c; /* Darker than darkCard */ padding: 12px; diff --git a/frontend/agentic-seek-front/src/App.js b/frontend/agentic-seek-front/src/App.js index 7da1a53..7521a70 100644 --- a/frontend/agentic-seek-front/src/App.js +++ b/frontend/agentic-seek-front/src/App.js @@ -242,9 +242,11 @@ function App() {

Tool: {block.tool_type}

{block.block}

Feedback: {block.feedback}

-

- Success: {block.success ? 'Yes' : 'No'} -

+ {block.success ? ( +

Success

+ ) : ( +

Failure

+ )} )) ) : ( From 713c01193fb513e1e68dd1d66c7190b330ec164c Mon Sep 17 00:00:00 2001 From: martin legrand Date: Fri, 16 May 2025 14:05:36 +0200 Subject: [PATCH 10/10] sec: no crossorigin allow --- api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api.py b/api.py index 194a29d..f326c39 100755 --- a/api.py +++ b/api.py @@ -34,7 +34,7 @@ config.read('config.ini') api.add_middleware( CORSMiddleware, - allow_origins=["*"], + allow_origins=["http://localhost", "http://localhost:3000"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], @@ -212,7 +212,6 @@ async def process_query(request: QueryRequest): query_resp.success = str(interaction.last_success) query_resp.blocks = blocks_json - # Store the raw dictionary representation query_resp_dict = { "done": query_resp.done, "answer": query_resp.answer,