This commit is contained in:
2025-12-22 23:30:36 +01:00
parent 5f7a821465
commit d97af2e253
3 changed files with 6 additions and 80 deletions

View File

@@ -182,36 +182,12 @@ class Browser {
try { try {
context.on('page', async (page) => { context.on('page', async (page) => {
try { try {
// CRITICAL: Install dialog handlers FIRST to prevent Bluetooth/Passkey native popups
page.removeAllListeners('dialog')
page.on('dialog', async (dialog) => {
const message = dialog.message()
const type = dialog.type()
this.bot.log(
this.bot.isMobile,
'BROWSER-DIALOG',
`Native dialog: [${type}] "${message.substring(0, 50)}"`,
'warn'
)
try {
if (type === 'beforeunload') {
await dialog.accept()
} else {
// Dismiss all other dialogs (Bluetooth, Passkey, Windows Hello)
await dialog.dismiss()
this.bot.log(this.bot.isMobile, 'BROWSER-DIALOG', `Dismissed ${type} dialog`, 'log', 'green')
}
} catch (e) {
this.bot.log(this.bot.isMobile, 'BROWSER-DIALOG', `Dialog error: ${e instanceof Error ? e.message : String(e)}`, 'error')
}
})
// CRITICAL: Inject anti-detection scripts BEFORE any page load // CRITICAL: Inject anti-detection scripts BEFORE any page load
await page.addInitScript(antiDetectScript) await page.addInitScript(antiDetectScript)
await page.addInitScript(timezoneScript) await page.addInitScript(timezoneScript)
// Virtual Authenticator support removed — no CDP WebAuthn setup performed here
// IMPROVED: Use crypto-secure random for viewport sizes // IMPROVED: Use crypto-secure random for viewport sizes
const { secureRandomInt } = await import('../util/security/SecureRandom') const { secureRandomInt } = await import('../util/security/SecureRandom')

View File

@@ -228,9 +228,6 @@ export class Login {
await this.disableFido(page) await this.disableFido(page)
// CRITICAL: Start automatic Escape sender to dismiss native OS dialogs
this.passkeyHandler.startEscapeWatcher(page)
const [reloadResult, totpResult, portalCheck] = await Promise.allSettled([ const [reloadResult, totpResult, portalCheck] = await Promise.allSettled([
this.bot.browser.utils.reloadBadPage(page), this.bot.browser.utils.reloadBadPage(page),
this.totpHandler.tryAutoTotp(page, 'initial landing'), this.totpHandler.tryAutoTotp(page, 'initial landing'),
@@ -262,12 +259,8 @@ export class Login {
this.bot.log(this.bot.isMobile, 'LOGIN', 'Login complete') this.bot.log(this.bot.isMobile, 'LOGIN', 'Login complete')
this.totpHandler.setTotpSecret(undefined) this.totpHandler.setTotpSecret(undefined)
// CRITICAL: Keep Escape watcher running for 10 more seconds this.bot.log(this.bot.isMobile, 'LOGIN', 'Login complete')
// Bluetooth/Windows Hello dialogs appear AFTER login completion this.totpHandler.setTotpSecret(undefined)
this.bot.log(this.bot.isMobile, 'LOGIN-ESCAPE', 'Keeping Escape watcher active for 10s (dialogs may appear after login)', 'log', 'cyan')
setTimeout(() => {
this.passkeyHandler.stopEscapeWatcher()
}, 10000)
} catch (e) { } catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e) const errorMessage = e instanceof Error ? e.message : String(e)
const stackTrace = e instanceof Error ? e.stack : undefined const stackTrace = e instanceof Error ? e.stack : undefined

View File

@@ -31,55 +31,12 @@ export class PasskeyHandler {
}).catch(logError('LOGIN-FIDO', 'Route interception setup failed', this.bot.isMobile)) }).catch(logError('LOGIN-FIDO', 'Route interception setup failed', this.bot.isMobile))
} }
private escapeWatcherInterval: NodeJS.Timeout | null = null // NOTE: Automatic Escape sender removed - native OS dialogs should be handled
// via safer server-side/workflow changes or Playwright Virtual Authenticator.
/**
* Start automatic Escape key sender to dismiss native OS dialogs (Bluetooth, Windows Hello)
* CRITICAL: Native system dialogs cannot be dismissed via Playwright dialog handlers
* They can only be closed with Escape key (as user discovered)
*/
public startEscapeWatcher(page: Page) {
// Stop any existing watcher
this.stopEscapeWatcher()
this.bot.log(this.bot.isMobile, 'LOGIN-ESCAPE', 'Starting automatic Escape sender (500ms interval)', 'log', 'cyan')
this.escapeWatcherInterval = setInterval(async () => {
try {
// Send Escape key to dismiss any native dialogs
await page.keyboard.press('Escape').catch(() => {
// Silent failure - page might be closed
})
} catch {
// Silent failure - interval will be cleared when stopEscapeWatcher is called
}
}, 500)
}
/**
* Stop the automatic Escape key sender
*/
public stopEscapeWatcher() {
if (this.escapeWatcherInterval) {
clearInterval(this.escapeWatcherInterval)
this.escapeWatcherInterval = null
this.bot.log(this.bot.isMobile, 'LOGIN-ESCAPE', 'Stopped automatic Escape sender', 'log', 'cyan')
}
}
public async handlePasskeyPrompts(page: Page, context: 'main' | 'oauth') { public async handlePasskeyPrompts(page: Page, context: 'main' | 'oauth') {
let did = false let did = false
// CRITICAL: Send Escape key FIRST to dismiss any native OS dialogs (Bluetooth, Windows Hello)
// These dialogs appear AFTER password/TOTP and cannot be dismissed via DOM clicks
try {
await page.keyboard.press('Escape')
await page.waitForTimeout(100) // Brief wait for dialog to close
this.bot.log(this.bot.isMobile, 'PASSKEY-ESCAPE', 'Sent Escape key to dismiss native dialogs', 'log', 'green')
} catch {
// Silent failure - page might not be ready
}
// Early exit for passkey creation flows (common on mobile): hit cancel/skip if present // Early exit for passkey creation flows (common on mobile): hit cancel/skip if present
const currentUrl = page.url() const currentUrl = page.url()
if (/fido\/create|passkey/i.test(currentUrl)) { if (/fido\/create|passkey/i.test(currentUrl)) {