jumpy-lion 0.1.6-beta.9 → 1.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -607
- package/browser/fonts/macos-bundle/41-macos-remaining.conf +157 -0
- package/browser/fonts/macos-bundle/README.md +24 -0
- package/browser/fonts/macos-bundle/build-metric-fonts.py +1010 -0
- package/browser/fonts/macos-bundle/generated/AmericanTypewriter-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/AmericanTypewriter-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/AndaleMono-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/AndaleMono-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/AndaleMono-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/AndaleMono-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/AppleChancery-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/AppleChancery-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/AppleChancery-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/AppleChancery-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Arial-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Arial-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Arial-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Arial-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/ArialBlack-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/ArialBlack-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/ArialBlack-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/ArialBlack-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/ArialNarrow-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/ArialNarrow-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/ArialNarrow-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/ArialNarrow-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Avenir-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Avenir-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Avenir-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Avenir-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/AvenirNext-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/AvenirNext-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/AvenirNext-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/AvenirNext-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/AvenirNextCondensed-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/AvenirNextCondensed-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/AvenirNextCondensed-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/AvenirNextCondensed-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Baskerville-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Baskerville-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Baskerville-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Baskerville-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/BigCaslon-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/BigCaslon-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/BigCaslon-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/BigCaslon-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/BlinkMacSystemFont-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/BlinkMacSystemFont-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/BlinkMacSystemFont-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/BlinkMacSystemFont-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Bodoni72-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Bodoni72-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Bodoni72-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Bodoni72-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Bodoni72Oldstyle-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Bodoni72Oldstyle-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Bodoni72Oldstyle-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Bodoni72Oldstyle-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Bodoni72Smallcaps-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Bodoni72Smallcaps-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Bodoni72Smallcaps-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Bodoni72Smallcaps-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/BradleyHand-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/BrushScriptMT-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Chalkboard-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Chalkboard-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Chalkboard-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Chalkboard-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/ChalkboardSE-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/ChalkboardSE-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/ChalkboardSE-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/ChalkboardSE-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Chalkduster-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Chalkduster-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Chalkduster-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Chalkduster-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Charter-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Charter-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Charter-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Charter-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Cochin-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Cochin-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Cochin-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Cochin-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/ComicSansMS-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/ComicSansMS-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/ComicSansMS-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/ComicSansMS-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Copperplate-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Copperplate-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Courier-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Courier-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Courier-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Courier-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/CourierNew-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Didot-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Didot-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Didot-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Didot-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Futura-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Futura-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Futura-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Futura-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Geneva-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Georgia-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Georgia-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Georgia-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Georgia-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/GillSans-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/GillSans-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/GillSans-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/GillSans-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Helvetica-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Helvetica-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Helvetica-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Helvetica-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/HelveticaNeue-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/HelveticaNeue-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/HelveticaNeue-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/HelveticaNeue-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Herculanum-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Herculanum-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Herculanum-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Herculanum-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/HoeflerText-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/HoeflerText-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/HoeflerText-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/HoeflerText-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Impact-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Impact-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Impact-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Impact-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/LucidaGrande-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/LucidaGrande-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/MarkerFelt-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/MarkerFelt-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/MarkerFelt-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/MarkerFelt-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Menlo-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Monaco-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Monaco-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Monaco-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Monaco-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Noteworthy-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Noteworthy-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Optima-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Optima-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Optima-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Optima-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Palatino-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Palatino-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Palatino-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Palatino-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Papyrus-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Papyrus-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Papyrus-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Papyrus-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/PartyLET-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/PartyLET-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/PartyLET-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/PartyLET-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Phosphate-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/README.md +132 -0
- package/browser/fonts/macos-bundle/generated/Rockwell-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Rockwell-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Rockwell-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Rockwell-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/SavoyeLET-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/SavoyeLET-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/SavoyeLET-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/SavoyeLET-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Seravek-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Seravek-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Seravek-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Seravek-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/SignPainter-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/SignPainter-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Skia-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Skia-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Skia-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Skia-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/SnellRoundhand-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/SnellRoundhand-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Tahoma-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Tahoma-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Times-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Times-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Times-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Times-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/TimesNewRoman-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/TimesNewRoman-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/TimesNewRoman-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/TimesNewRoman-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Trattatello-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/TrebuchetMS-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/TrebuchetMS-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/TrebuchetMS-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/TrebuchetMS-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Verdana-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Verdana-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Verdana-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Verdana-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Webdings-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Webdings-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Webdings-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Webdings-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Zapfino-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Zapfino-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Zapfino-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/Zapfino-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/manifest.json +2928 -0
- package/browser/fonts/macos-bundle/generated/systemui-Bold.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/systemui-BoldItalic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/systemui-Italic.ttf +0 -0
- package/browser/fonts/macos-bundle/generated/systemui-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/install.sh +136 -0
- package/browser/fonts/macos-bundle/vendor/README.md +30 -0
- package/browser/fonts/macos-bundle/vendor/anton/Anton-Regular.ttf +0 -0
- package/browser/fonts/macos-bundle/vendor/anton/OFL.txt +93 -0
- package/browser/fonts/macos-bundle/vendor/gelasio/Gelasio-Italic-VariableFont_wght.ttf +0 -0
- package/browser/fonts/macos-bundle/vendor/gelasio/Gelasio-VariableFont_wght.ttf +0 -0
- package/browser/fonts/macos-bundle/vendor/gelasio/OFL.txt +93 -0
- package/browser/fonts/macos-bundle/verify-metric-fonts.py +171 -0
- package/dist/browser-controller.d.ts +47 -4
- package/dist/browser-controller.d.ts.map +1 -1
- package/dist/browser-controller.js +219 -278
- package/dist/browser-controller.js.map +1 -1
- package/dist/browser-plugin.d.ts +82 -110
- package/dist/browser-plugin.d.ts.map +1 -1
- package/dist/browser-plugin.js +405 -379
- package/dist/browser-plugin.js.map +1 -1
- package/dist/browser-process/align-fingerprint-config.d.ts +36 -0
- package/dist/browser-process/align-fingerprint-config.d.ts.map +1 -0
- package/dist/browser-process/align-fingerprint-config.js +74 -0
- package/dist/browser-process/align-fingerprint-config.js.map +1 -0
- package/dist/browser-process/anti-detect-browser.d.ts +2 -2
- package/dist/browser-process/anti-detect-browser.js +7 -7
- package/dist/browser-process/anti-detect-config.d.ts +146 -2
- package/dist/browser-process/anti-detect-config.d.ts.map +1 -1
- package/dist/browser-process/anti-detect-config.js +432 -127
- package/dist/browser-process/anti-detect-config.js.map +1 -1
- package/dist/browser-process/browser.d.ts +25 -92
- package/dist/browser-process/browser.d.ts.map +1 -1
- package/dist/browser-process/browser.js +467 -79
- package/dist/browser-process/browser.js.map +1 -1
- package/dist/browser-process/fingerprint-config.d.ts +146 -0
- package/dist/browser-process/fingerprint-config.d.ts.map +1 -0
- package/dist/browser-process/fingerprint-config.js +351 -0
- package/dist/browser-process/fingerprint-config.js.map +1 -0
- package/dist/browser-process/get-chrome-executable.d.ts +6 -0
- package/dist/browser-process/get-chrome-executable.d.ts.map +1 -1
- package/dist/browser-process/get-chrome-executable.js +79 -4
- package/dist/browser-process/get-chrome-executable.js.map +1 -1
- package/dist/browser-process/gpu-family-profiles.d.ts +58 -0
- package/dist/browser-process/gpu-family-profiles.d.ts.map +1 -0
- package/dist/browser-process/gpu-family-profiles.js +480 -0
- package/dist/browser-process/gpu-family-profiles.js.map +1 -0
- package/dist/browser-process/launch-options.d.ts +301 -0
- package/dist/browser-process/launch-options.d.ts.map +1 -0
- package/dist/browser-process/launch-options.js +2 -0
- package/dist/browser-process/launch-options.js.map +1 -0
- package/dist/browser-process/port-allocator.d.ts +64 -0
- package/dist/browser-process/port-allocator.d.ts.map +1 -0
- package/dist/browser-process/port-allocator.js +198 -0
- package/dist/browser-process/port-allocator.js.map +1 -0
- package/dist/browser-process/process.d.ts +85 -0
- package/dist/browser-process/process.d.ts.map +1 -1
- package/dist/browser-process/process.js +235 -19
- package/dist/browser-process/process.js.map +1 -1
- package/dist/browser-process/signal-cleanup.d.ts +40 -0
- package/dist/browser-process/signal-cleanup.d.ts.map +1 -0
- package/dist/browser-process/signal-cleanup.js +105 -0
- package/dist/browser-process/signal-cleanup.js.map +1 -0
- package/dist/browser-process/widevine-cdm.d.ts +144 -0
- package/dist/browser-process/widevine-cdm.d.ts.map +1 -0
- package/dist/browser-process/widevine-cdm.js +298 -0
- package/dist/browser-process/widevine-cdm.js.map +1 -0
- package/dist/browser-profiles/chrome/populate-profile.d.ts.map +1 -1
- package/dist/browser-profiles/chrome/populate-profile.js +21 -22
- package/dist/browser-profiles/chrome/populate-profile.js.map +1 -1
- package/dist/connection/cdp-reconnection.d.ts +50 -0
- package/dist/connection/cdp-reconnection.d.ts.map +1 -0
- package/dist/connection/cdp-reconnection.js +75 -0
- package/dist/connection/cdp-reconnection.js.map +1 -0
- package/dist/connection/page-session.d.ts +114 -0
- package/dist/connection/page-session.d.ts.map +1 -0
- package/dist/connection/page-session.js +237 -0
- package/dist/connection/page-session.js.map +1 -0
- package/dist/connection/session-client.d.ts +34 -0
- package/dist/connection/session-client.d.ts.map +1 -0
- package/dist/connection/session-client.js +129 -0
- package/dist/connection/session-client.js.map +1 -0
- package/dist/crawler.d.ts +9 -88
- package/dist/crawler.d.ts.map +1 -1
- package/dist/crawler.js +157 -8
- package/dist/crawler.js.map +1 -1
- package/dist/fingerprinting/fingerprint-injector.d.ts +24 -114
- package/dist/fingerprinting/fingerprint-injector.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-injector.js +305 -611
- package/dist/fingerprinting/fingerprint-injector.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.d.ts +1 -1
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.js +16 -14
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/index.d.ts +6 -2
- package/dist/fingerprinting/fingerprint-overrides/index.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/index.js +6 -2
- package/dist/fingerprinting/fingerprint-overrides/index.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.d.ts +1 -1
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.js +4 -4
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/locale-spoofing.js +4 -4
- package/dist/fingerprinting/fingerprint-overrides/navigator-override.d.ts +18 -0
- package/dist/fingerprinting/fingerprint-overrides/navigator-override.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/navigator-override.js +136 -0
- package/dist/fingerprinting/fingerprint-overrides/navigator-override.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/override.d.ts +151 -0
- package/dist/fingerprinting/fingerprint-overrides/override.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/override.js +14 -0
- package/dist/fingerprinting/fingerprint-overrides/override.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/platform-consistency.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/platform-consistency.js +46 -15
- package/dist/fingerprinting/fingerprint-overrides/platform-consistency.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/registry.d.ts +43 -0
- package/dist/fingerprinting/fingerprint-overrides/registry.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/registry.js +342 -0
- package/dist/fingerprinting/fingerprint-overrides/registry.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/screen-override.d.ts +16 -0
- package/dist/fingerprinting/fingerprint-overrides/screen-override.d.ts.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/screen-override.js +175 -0
- package/dist/fingerprinting/fingerprint-overrides/screen-override.js.map +1 -0
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.d.ts +1 -1
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.js +4 -4
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.js +75 -9
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/ua-ch.d.ts +1 -1
- package/dist/fingerprinting/fingerprint-overrides/ua-ch.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/ua-ch.js +34 -20
- package/dist/fingerprinting/fingerprint-overrides/ua-ch.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.js +28 -9
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webrtc-spoofing.js +1 -1
- package/dist/fingerprinting/hardware-realism.d.ts +74 -0
- package/dist/fingerprinting/hardware-realism.d.ts.map +1 -0
- package/dist/fingerprinting/hardware-realism.js +155 -0
- package/dist/fingerprinting/hardware-realism.js.map +1 -0
- package/dist/fingerprinting/injection-planner.d.ts +133 -0
- package/dist/fingerprinting/injection-planner.d.ts.map +1 -0
- package/dist/fingerprinting/injection-planner.js +497 -0
- package/dist/fingerprinting/injection-planner.js.map +1 -0
- package/dist/fingerprinting/locale-resolver.d.ts +8 -1
- package/dist/fingerprinting/locale-resolver.d.ts.map +1 -1
- package/dist/fingerprinting/locale-resolver.js +26 -3
- package/dist/fingerprinting/locale-resolver.js.map +1 -1
- package/dist/fingerprinting/non-apify-fingerprint-generator.d.ts +4 -4
- package/dist/fingerprinting/non-apify-fingerprint-generator.d.ts.map +1 -1
- package/dist/fingerprinting/non-apify-fingerprint-generator.js +35 -69
- package/dist/fingerprinting/non-apify-fingerprint-generator.js.map +1 -1
- package/dist/fingerprinting/non-apify-profiles.json +10515 -8494
- package/dist/fingerprinting/os-consistency.d.ts +28 -0
- package/dist/fingerprinting/os-consistency.d.ts.map +1 -0
- package/dist/fingerprinting/os-consistency.js +45 -0
- package/dist/fingerprinting/os-consistency.js.map +1 -0
- package/dist/fingerprinting/profile-quality.d.ts +25 -0
- package/dist/fingerprinting/profile-quality.d.ts.map +1 -0
- package/dist/fingerprinting/profile-quality.js +310 -0
- package/dist/fingerprinting/profile-quality.js.map +1 -0
- package/dist/fingerprinting/profile-selector.d.ts +102 -0
- package/dist/fingerprinting/profile-selector.d.ts.map +1 -0
- package/dist/fingerprinting/profile-selector.js +168 -0
- package/dist/fingerprinting/profile-selector.js.map +1 -0
- package/dist/fingerprinting/ua-alignment.d.ts +83 -0
- package/dist/fingerprinting/ua-alignment.d.ts.map +1 -0
- package/dist/fingerprinting/ua-alignment.js +197 -0
- package/dist/fingerprinting/ua-alignment.js.map +1 -0
- package/dist/fingerprinting/ua-ch-brands.d.ts +88 -0
- package/dist/fingerprinting/ua-ch-brands.d.ts.map +1 -0
- package/dist/fingerprinting/ua-ch-brands.js +174 -0
- package/dist/fingerprinting/ua-ch-brands.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/input/dropdown-selector.d.ts +90 -0
- package/dist/input/dropdown-selector.d.ts.map +1 -0
- package/dist/input/dropdown-selector.js +394 -0
- package/dist/input/dropdown-selector.js.map +1 -0
- package/dist/input/element-target.d.ts +152 -0
- package/dist/input/element-target.d.ts.map +1 -0
- package/dist/input/element-target.js +461 -0
- package/dist/input/element-target.js.map +1 -0
- package/dist/input/input-emulator.d.ts +85 -0
- package/dist/input/input-emulator.d.ts.map +1 -0
- package/dist/input/input-emulator.js +325 -0
- package/dist/input/input-emulator.js.map +1 -0
- package/dist/input/input-transport.d.ts +60 -0
- package/dist/input/input-transport.d.ts.map +1 -0
- package/dist/input/input-transport.js +28 -0
- package/dist/input/input-transport.js.map +1 -0
- package/dist/input/recording-transport.d.ts +32 -0
- package/dist/input/recording-transport.d.ts.map +1 -0
- package/dist/input/recording-transport.js +43 -0
- package/dist/input/recording-transport.js.map +1 -0
- package/dist/keyboard-layout.d.ts +2 -2
- package/dist/keyboard-layout.js +2 -2
- package/dist/navigation/page-navigation.d.ts +67 -0
- package/dist/navigation/page-navigation.d.ts.map +1 -0
- package/dist/navigation/page-navigation.js +122 -0
- package/dist/navigation/page-navigation.js.map +1 -0
- package/dist/network/network-watch.d.ts +110 -0
- package/dist/network/network-watch.d.ts.map +1 -0
- package/dist/network/network-watch.js +223 -0
- package/dist/network/network-watch.js.map +1 -0
- package/dist/page.d.ts +123 -104
- package/dist/page.d.ts.map +1 -1
- package/dist/page.js +348 -1129
- package/dist/page.js.map +1 -1
- package/dist/session/inject-hook.d.ts +24 -0
- package/dist/session/inject-hook.d.ts.map +1 -0
- package/dist/session/inject-hook.js +80 -0
- package/dist/session/inject-hook.js.map +1 -0
- package/dist/session/save.d.ts +21 -0
- package/dist/session/save.d.ts.map +1 -0
- package/dist/session/save.js +163 -0
- package/dist/session/save.js.map +1 -0
- package/dist/session/tar-data-dir.d.ts +18 -0
- package/dist/session/tar-data-dir.d.ts.map +1 -0
- package/dist/session/tar-data-dir.js +105 -0
- package/dist/session/tar-data-dir.js.map +1 -0
- package/dist/session/types.d.ts +80 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +29 -0
- package/dist/session/types.js.map +1 -0
- package/dist/session-profile.d.ts +102 -0
- package/dist/session-profile.d.ts.map +1 -0
- package/dist/session-profile.js +163 -0
- package/dist/session-profile.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +15 -6
- package/dist/fingerprinting/custom-fingerprint-injector.d.ts +0 -87
- package/dist/fingerprinting/custom-fingerprint-injector.d.ts.map +0 -1
- package/dist/fingerprinting/custom-fingerprint-injector.js +0 -342
- package/dist/fingerprinting/custom-fingerprint-injector.js.map +0 -1
- package/dist/launcher-wrap.d.ts +0 -10
- package/dist/launcher-wrap.d.ts.map +0 -1
- package/dist/launcher-wrap.js +0 -11
- package/dist/launcher-wrap.js.map +0 -1
|
@@ -3,12 +3,13 @@ import fs from 'fs/promises';
|
|
|
3
3
|
import os from 'os';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { log } from '@crawlee/core';
|
|
6
|
-
import {
|
|
6
|
+
import { isAntiDetectBrowserSync } from './anti-detect-browser.js';
|
|
7
|
+
import { writeTempFingerprintConfig } from './anti-detect-config.js';
|
|
8
|
+
import { getChromeExecutablePath, getAntiDetectBrowserPath } from './get-chrome-executable.js';
|
|
7
9
|
import { BrowserProcess } from './process.js';
|
|
10
|
+
import { seedWidevineCdm } from './widevine-cdm.js';
|
|
8
11
|
import { populateChromeProfile } from '../browser-profiles/chrome/populate-profile.js';
|
|
9
12
|
import { createChromeProfile } from '../browser-profiles/index.js';
|
|
10
|
-
import { isAntiDetectBrowserSync } from './anti-detect-browser.js';
|
|
11
|
-
import { writeTempFingerprintConfig } from './anti-detect-config.js';
|
|
12
13
|
const EXTENSION_PATH = path.resolve(import.meta.dirname, '../fingerprinting/all-fingerprint-defender');
|
|
13
14
|
const EXTENSION_PATH2 = path.resolve(import.meta.dirname, '../fingerprinting/anti-webgpu');
|
|
14
15
|
// --lang is intentionally NOT hardcoded below. It is appended per-session from the
|
|
@@ -18,25 +19,35 @@ const EXTENSION_PATH2 = path.resolve(import.meta.dirname, '../fingerprinting/ant
|
|
|
18
19
|
// Flag policy (see ChromeDriver default-switch audit in git history for full evidence):
|
|
19
20
|
//
|
|
20
21
|
// Dropped as actively harmful:
|
|
21
|
-
// --enable-features=NetworkService,NetworkServiceInProcess
|
|
22
|
+
// --enable-features=NetworkService,NetworkServiceInProcess - desktop default is DISABLED
|
|
22
23
|
// since M89; forcing it creates a runtime state no real user has.
|
|
23
|
-
// --disable-ipc-flooding-protection
|
|
24
|
+
// --disable-ipc-flooding-protection - JS-probeable via 201+ history.pushState()/10s.
|
|
24
25
|
//
|
|
25
26
|
// Dropped because they have a concrete page-JS detection probe:
|
|
26
|
-
// --disable-popup-blocking
|
|
27
|
-
// --disable-
|
|
28
|
-
// --disable-backgrounding-occluded-windows — same surface; internally named
|
|
29
|
-
// kDisableBackgroundingOccludedWindowsForTesting.
|
|
30
|
-
// --disable-component-extensions-with-background-pages — speechSynthesis.getVoices()
|
|
27
|
+
// --disable-popup-blocking - window.open() null-vs-non-null from a timer.
|
|
28
|
+
// --disable-component-extensions-with-background-pages - speechSynthesis.getVoices()
|
|
31
29
|
// leaks the missing TTS component extension.
|
|
32
30
|
//
|
|
31
|
+
// Conditionally added based on which binary we launch:
|
|
32
|
+
// --disable-renderer-backgrounding + --disable-backgrounding-occluded-windows
|
|
33
|
+
// auto-enabled when launching the anti-detect binary; the binary's
|
|
34
|
+
// direct source-tree edit to document.visibilityState / document.hidden
|
|
35
|
+
// hides the rAF / setTimeout throttling tell, so these flags are safe.
|
|
36
|
+
// On vanilla Chrome they remain opt-in via CHROMIUM_KEEP_BACKGROUNDING=1.
|
|
37
|
+
// macOS's own compositor-pause-on-occlusion path is in
|
|
38
|
+
// WebContentsViewCocoa and has no command-line switch - the anti-detect
|
|
39
|
+
// binary drops the kOccluded transition directly in
|
|
40
|
+
// content/app_shim_remote_cocoa/web_contents_view_cocoa.mm.
|
|
41
|
+
//
|
|
33
42
|
// Dropped because they are provably dead or placebo:
|
|
34
|
-
// --disable-translate
|
|
35
|
-
// --disable-infobars
|
|
43
|
+
// --disable-translate - string absent from Chromium switches files; noop since ~2018.
|
|
44
|
+
// --disable-infobars - infobar_manager.cc:22-31 gate requires --headless OR a
|
|
36
45
|
// Chrome-for-Testing build; never active in our setup.
|
|
37
|
-
// --disable-blink-features=AutomationControlled
|
|
46
|
+
// --disable-blink-features=AutomationControlled - only blocks the disabled-by-default
|
|
38
47
|
// RuntimeEnabledFeatures path; the actual webdriver-setter is
|
|
39
|
-
// probe::ApplyAutomationOverride via CDP, which
|
|
48
|
+
// probe::ApplyAutomationOverride via CDP, which the anti-detect binary's
|
|
49
|
+
// direct edit to inspector_emulation_agent.cc / navigator.cc already no-ops
|
|
50
|
+
// (Navigator::webdriver() hardcoded to false).
|
|
40
51
|
//
|
|
41
52
|
// Dropped because they are network-signature flags absent from modern stealth tooling
|
|
42
53
|
// (nodriver / Patchright) with no page-JS benefit on our setup:
|
|
@@ -48,6 +59,21 @@ const EXTENSION_PATH2 = path.resolve(import.meta.dirname, '../fingerprinting/ant
|
|
|
48
59
|
* (a) no known page-JS observable probe, AND
|
|
49
60
|
* (b) an operational or consistency justification (avoid startup dialogs, reduce
|
|
50
61
|
* automation-irrelevant background work, match realistic fresh-profile defaults).
|
|
62
|
+
*
|
|
63
|
+
* Two operationally-detectable flags moved to opt-in env vars:
|
|
64
|
+
* - --disable-renderer-backgrounding / --disable-backgrounding-occluded-windows
|
|
65
|
+
* (rAF throttle probe): opt in via CHROMIUM_KEEP_BACKGROUNDING=1 for local
|
|
66
|
+
* headfull dev where window focus loss would otherwise stall the crawl. The
|
|
67
|
+
* document.hidden() C++ override (cromite patch D26) handles the JS-property
|
|
68
|
+
* side of the visibility spoof regardless.
|
|
69
|
+
* - --use-angle=swiftshader (WebGL perf probe + tell on macOS): opt in via
|
|
70
|
+
* CHROMIUM_USE_SWIFTSHADER=1 for canvas/WebGL pixel parity between local-mac
|
|
71
|
+
* (Metal-capable) and Apify-linux. Off by default lets ANGLE Metal/D3D/GL
|
|
72
|
+
* run natively. The anti-detect binary's direct source-tree edit to
|
|
73
|
+
* WebGLRenderingContextBase::getParameter (in
|
|
74
|
+
* third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc)
|
|
75
|
+
* still spoofs UNMASKED_VENDOR_WEBGL / UNMASKED_RENDERER_WEBGL regardless
|
|
76
|
+
* of which backend ANGLE picks.
|
|
51
77
|
*/
|
|
52
78
|
const UNIVERSAL_STEALTH_ARGS = [
|
|
53
79
|
// CDP transport: Chrome 111+ requires an origin allow-list for WebSocket connections
|
|
@@ -61,28 +87,118 @@ const UNIVERSAL_STEALTH_ARGS = [
|
|
|
61
87
|
'--disable-session-crashed-bubble',
|
|
62
88
|
// Suppress the EU search-engine choice modal, which would block a fresh profile.
|
|
63
89
|
'--disable-search-engine-choice-screen',
|
|
64
|
-
// Predictable startup URL
|
|
90
|
+
// Predictable startup URL - blank page instead of NTP fetch.
|
|
65
91
|
'--homepage=about:blank',
|
|
66
92
|
// Operational: do not show the "Page Unresponsive" dialog mid-crawl. No JS probe.
|
|
67
93
|
'--disable-hang-monitor',
|
|
68
94
|
// Operational: suppress the "Confirm form resubmission" dialog on POST reload. No JS probe.
|
|
69
95
|
'--disable-prompt-on-repost',
|
|
70
96
|
];
|
|
97
|
+
/**
|
|
98
|
+
* Optional flags gated on env vars. Adding either of these to the launch argv
|
|
99
|
+
* narrows operational pain (headfull focus-loss stalls; canvas parity drift)
|
|
100
|
+
* but adds a fingerprint surface that real Chrome users do not have.
|
|
101
|
+
*
|
|
102
|
+
* Default-off so production crawls ship the cleanest argv we can manage. Local
|
|
103
|
+
* dev opts in via env. The anti-detect binary path also auto-enables the
|
|
104
|
+
* backgrounding flags - see `getAntiDetectBackgroundingArgs`.
|
|
105
|
+
*/
|
|
106
|
+
function getOptionalStealthArgs() {
|
|
107
|
+
const out = [];
|
|
108
|
+
if (process.env.CHROMIUM_KEEP_BACKGROUNDING === '1'
|
|
109
|
+
|| process.env.CHROMIUM_KEEP_BACKGROUNDING === 'true') {
|
|
110
|
+
// Keep the renderer running at full speed when the OS window loses focus
|
|
111
|
+
// or is occluded. Without these, headfull dev stalls when the user
|
|
112
|
+
// alt-tabs away - Chrome drops renderer priority and clamps setTimeout
|
|
113
|
+
// to ≥1s. (render_process_host_impl.cc; web_contents_impl.cc.)
|
|
114
|
+
//
|
|
115
|
+
// Trade-off: rAF/setTimeout drift while document.hidden===true is JS-probeable.
|
|
116
|
+
out.push('--disable-renderer-backgrounding');
|
|
117
|
+
out.push('--disable-backgrounding-occluded-windows');
|
|
118
|
+
}
|
|
119
|
+
if (process.env.CHROMIUM_USE_SWIFTSHADER === '1'
|
|
120
|
+
|| process.env.CHROMIUM_USE_SWIFTSHADER === 'true') {
|
|
121
|
+
// Force ANGLE → SwiftShader so canvas/WebGL pixel output is identical
|
|
122
|
+
// between local-mac (Metal-capable) and Apify-linux (no GPU). Cromite's
|
|
123
|
+
// seeded pixel noise is a delta on top of a baseline; the baseline must
|
|
124
|
+
// match to make the final hash reproducible across hosts.
|
|
125
|
+
//
|
|
126
|
+
// Trade-off: WebGL perf benchmarks (timing a heavy scene) can detect
|
|
127
|
+
// SwiftShader on macOS hardware. Mainstream anti-bot vendors hash
|
|
128
|
+
// statically, but the flag is also visible in /proc/<pid>/cmdline on
|
|
129
|
+
// Linux when --no-sandbox is active.
|
|
130
|
+
out.push('--use-angle=swiftshader');
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Backgrounding/occlusion suppression. Auto-enabled when launching the
|
|
136
|
+
* cromite anti-detect binary because cromite patch D26 pins
|
|
137
|
+
* `document.visibilityState` / `document.hidden` to "visible" at the
|
|
138
|
+
* Blink layer, so the rAF/setTimeout drift these flags would expose on
|
|
139
|
+
* vanilla Chrome is invisible to page JS under the patched binary.
|
|
140
|
+
*
|
|
141
|
+
* On macOS the equivalent of `CalculateNativeWinOcclusion` (the AURA
|
|
142
|
+
* Linux/Windows path) is `WebContentsViewCocoa.updateWebContentsVisibility`
|
|
143
|
+
* which has NO command-line kill switch - the kOccluded transition is
|
|
144
|
+
* hardcoded in content/app_shim_remote_cocoa/web_contents_view_cocoa.mm.
|
|
145
|
+
* That path is patched out in the cromite binary directly (see the
|
|
146
|
+
* "Anti-Detect: drop the kOccluded branch" patch), so no darwin-only
|
|
147
|
+
* launch flag is needed (or available) for the same effect.
|
|
148
|
+
*
|
|
149
|
+
* What these flags still do on macOS:
|
|
150
|
+
* --disable-renderer-backgrounding stops the renderer-process priority
|
|
151
|
+
* drop when our window is not the key window (CDP-driven sessions
|
|
152
|
+
* often never become key).
|
|
153
|
+
* --disable-backgrounding-occluded-windows stops the priority drop
|
|
154
|
+
* specifically on occlusion (separate from the visibility transition
|
|
155
|
+
* fixed by the cromite Cocoa patch).
|
|
156
|
+
*/
|
|
157
|
+
function getAntiDetectBackgroundingArgs(_platform) {
|
|
158
|
+
return [
|
|
159
|
+
'--disable-renderer-backgrounding',
|
|
160
|
+
'--disable-backgrounding-occluded-windows',
|
|
161
|
+
];
|
|
162
|
+
}
|
|
71
163
|
/**
|
|
72
164
|
* Linux-specific flags. Necessary for server-side operation; no page-JS probe on any:
|
|
73
165
|
* --no-sandbox: running as root or in a rootless container where seccomp-bpf is
|
|
74
166
|
* unavailable. Not observable from page JS (/proc/self/status is not reachable
|
|
75
|
-
* from the renderer).
|
|
76
|
-
*
|
|
167
|
+
* from the renderer). Can be opted out with $CHROMIUM_KEEP_SANDBOX=1 when the
|
|
168
|
+
* container is rootless AND the host kernel allows user namespaces - e.g.
|
|
169
|
+
* modern Apify runners that drop privileges to UID 1000. The kernel-level
|
|
170
|
+
* sandbox is a small extra defense and removing the --no-sandbox flag
|
|
171
|
+
* reduces /proc/<pid>/cmdline noise.
|
|
172
|
+
* --disable-dev-shm-usage: Docker's default /dev/shm is 64 MB - renderers crash when
|
|
77
173
|
* it fills. Backing switches to /tmp. Not observable from page JS.
|
|
78
174
|
* --password-store=basic: avoid blocking on gnome-keyring/kwallet prompts. Not
|
|
79
175
|
* observable from page JS.
|
|
80
176
|
*/
|
|
81
|
-
const
|
|
82
|
-
'--no-sandbox',
|
|
177
|
+
const LINUX_STEALTH_ARGS_BASE = [
|
|
83
178
|
'--disable-dev-shm-usage',
|
|
84
179
|
'--password-store=basic',
|
|
85
180
|
];
|
|
181
|
+
/**
|
|
182
|
+
* Build the Linux flag list at runtime so the $CHROMIUM_KEEP_SANDBOX opt-out is
|
|
183
|
+
* picked up per-launch (process env can change between calls in long-running
|
|
184
|
+
* processes, e.g. when an Apify actor sets the var at startup).
|
|
185
|
+
*
|
|
186
|
+
* `--disable-setuid-sandbox` ships alongside `--no-sandbox`: even with
|
|
187
|
+
* `--no-sandbox`, Chrome may try to call the setuid sandbox helper
|
|
188
|
+
* (chrome_sandbox / chrome-sandbox) at startup; on restricted containers
|
|
189
|
+
* (Apify LIMITED_PERMISSIONS, Docker rootless) the helper isn't installed
|
|
190
|
+
* or can't bind, and Chrome hangs waiting on it instead of crashing -
|
|
191
|
+
* presents to the caller as a silent CDP-connect timeout. When
|
|
192
|
+
* CHROMIUM_KEEP_SANDBOX=1, both flags drop so the user-namespace sandbox
|
|
193
|
+
* can engage normally.
|
|
194
|
+
*/
|
|
195
|
+
function getLinuxStealthArgs() {
|
|
196
|
+
const keepSandbox = process.env.CHROMIUM_KEEP_SANDBOX === '1'
|
|
197
|
+
|| process.env.CHROMIUM_KEEP_SANDBOX === 'true';
|
|
198
|
+
return keepSandbox
|
|
199
|
+
? LINUX_STEALTH_ARGS_BASE
|
|
200
|
+
: ['--no-sandbox', '--disable-setuid-sandbox', ...LINUX_STEALTH_ARGS_BASE];
|
|
201
|
+
}
|
|
86
202
|
/**
|
|
87
203
|
* macOS-specific flags. `--use-mock-keychain` substitutes the real Keychain with an
|
|
88
204
|
* in-memory mock to avoid blocking-permission dialogs. Not observable from page JS.
|
|
@@ -94,28 +210,63 @@ const DARWIN_STEALTH_ARGS = [
|
|
|
94
210
|
* Build the stealth argv list. Exported for unit tests so we can assert exact flag
|
|
95
211
|
* sets without booting a browser.
|
|
96
212
|
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
213
|
+
* When `usingAntiDetectBrowser` is true, the renderer-backgrounding and
|
|
214
|
+
* (on macOS) native-window-occlusion features are disabled regardless of
|
|
215
|
+
* env vars, because cromite patch D26 hides the only page-JS tell those
|
|
216
|
+
* flags would otherwise expose (document.hidden / rAF throttling). This
|
|
217
|
+
* fixes the "page goes blank until I refocus the window" symptom on
|
|
218
|
+
* headful macOS launches driven by CDP.
|
|
100
219
|
*/
|
|
101
220
|
export function buildStealthArgs(env = {}) {
|
|
102
221
|
const platform = env.platform ?? process.platform;
|
|
103
|
-
const args = [...UNIVERSAL_STEALTH_ARGS];
|
|
222
|
+
const args = [...UNIVERSAL_STEALTH_ARGS, ...getOptionalStealthArgs()];
|
|
223
|
+
if (env.usingAntiDetectBrowser) {
|
|
224
|
+
for (const flag of getAntiDetectBackgroundingArgs(platform)) {
|
|
225
|
+
if (!args.includes(flag))
|
|
226
|
+
args.push(flag);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
104
229
|
if (platform === 'linux')
|
|
105
|
-
args.push(...
|
|
230
|
+
args.push(...getLinuxStealthArgs());
|
|
106
231
|
if (platform === 'darwin')
|
|
107
232
|
args.push(...DARWIN_STEALTH_ARGS);
|
|
108
233
|
return args;
|
|
109
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* Process-layer launch options: everything in BaseLaunchBrowserOptions
|
|
237
|
+
* (the single shared declaration in launch-options.ts) plus the spawn-time
|
|
238
|
+
* fields only this layer knows about.
|
|
239
|
+
*/
|
|
240
|
+
/**
|
|
241
|
+
* Resolve the platform-overrides config for an anti-detect browser launch, or
|
|
242
|
+
* THROW. The anti-detect binary exists to spoof; launching it without a config
|
|
243
|
+
* silently ships the host identity (navigator.platform / userAgent / WebGL all
|
|
244
|
+
* real) - the "30% of sessions are detectable and nobody notices" failure mode.
|
|
245
|
+
*
|
|
246
|
+
* Resolution order: the per-session config (stamped by browser-plugin.ts) first,
|
|
247
|
+
* then the static browser/config/default_fingerprint.json. If both are absent
|
|
248
|
+
* (per-session missing AND the default loader fails or yields nothing), this
|
|
249
|
+
* throws so the launch aborts instead of leaking. `loadStaticDefault` is injected
|
|
250
|
+
* so the dynamic-import path can be scripted in tests.
|
|
251
|
+
*/
|
|
252
|
+
export async function resolveAntiDetectFingerprintConfig(perSessionConfig, loadStaticDefault) {
|
|
253
|
+
const resolved = perSessionConfig ?? await loadStaticDefault().catch(() => null);
|
|
254
|
+
if (resolved == null) {
|
|
255
|
+
throw new Error('[AntiDetect] No fingerprint config resolved for the anti-detect browser '
|
|
256
|
+
+ '(per-session config absent and browser/config/default_fingerprint.json unavailable). '
|
|
257
|
+
+ 'Refusing to launch unspoofed - the session would leak the host platform, user agent, and WebGL identity.');
|
|
258
|
+
}
|
|
259
|
+
return resolved;
|
|
260
|
+
}
|
|
110
261
|
export const launchBrowserWithNewProfile = async (executablePath, options = {}) => {
|
|
111
262
|
const { anonymizedProxyUrl, debuggingPort, windowWidth = 1920, windowHeight = 1080, ignoreHttpsErrors, headless, useExperimentalFingerprints = false, maximizeWindow = true, enableStealthMode = true, populateProfile: shouldPopulateProfile = true, userDataDir: providedUserDataDir, keepUserDataDir, dnsOverHttpsServer, secureDnsMode, webrtcIpHandlingPolicy, useNativeStealth = false, } = options;
|
|
112
263
|
// Detect if we're running with the anti-detect browser binary
|
|
113
264
|
const usingAntiDetectBrowser = useNativeStealth && isAntiDetectBrowserSync(executablePath);
|
|
114
265
|
if (useNativeStealth && !usingAntiDetectBrowser) {
|
|
115
|
-
log.debug('[AntiDetect] useNativeStealth requested but anti-detect browser not detected
|
|
266
|
+
log.debug('[AntiDetect] useNativeStealth requested but anti-detect browser not detected - using standard Chrome');
|
|
116
267
|
}
|
|
117
268
|
else if (usingAntiDetectBrowser) {
|
|
118
|
-
log.debug('[AntiDetect] Anti-detect browser detected
|
|
269
|
+
log.debug('[AntiDetect] Anti-detect browser detected - native C++ stealth active');
|
|
119
270
|
}
|
|
120
271
|
// Track temp config file for cleanup
|
|
121
272
|
let fingerprintConfigPath = null;
|
|
@@ -127,8 +278,12 @@ export const launchBrowserWithNewProfile = async (executablePath, options = {})
|
|
|
127
278
|
if (providedUserDataDir) {
|
|
128
279
|
await fs.mkdir(userDataDir, { recursive: true });
|
|
129
280
|
}
|
|
130
|
-
|
|
131
|
-
//
|
|
281
|
+
// Populate profile with realistic user data for 2024-2025 anti-bot evasion.
|
|
282
|
+
// populateChromeProfile is the sole Preferences writer here: it builds the
|
|
283
|
+
// merged Preferences object in memory and writes it once (no separate
|
|
284
|
+
// createChromeProfile write to immediately overwrite). createChromeProfile
|
|
285
|
+
// is only used when population is disabled, or as a fallback if population
|
|
286
|
+
// fails partway so the launch still has a valid base profile.
|
|
132
287
|
if (shouldPopulateProfile) {
|
|
133
288
|
try {
|
|
134
289
|
await populateChromeProfile(userDataDir, {
|
|
@@ -140,6 +295,22 @@ export const launchBrowserWithNewProfile = async (executablePath, options = {})
|
|
|
140
295
|
}
|
|
141
296
|
catch (popError) {
|
|
142
297
|
log.debug(`Could not populate profile: ${popError}`);
|
|
298
|
+
await createChromeProfile(userDataDir).catch(() => { });
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
await createChromeProfile(userDataDir);
|
|
303
|
+
}
|
|
304
|
+
// Seed the Widevine CDM from the per-host cache before Chromium starts, so
|
|
305
|
+
// startup registration picks it up and `requestMediaKeySystemAccess` resolves
|
|
306
|
+
// on the first navigation, exactly as it does in a real Chrome. On Linux this
|
|
307
|
+
// is the ONLY thing that works: the CDM is bound at startup there, so a copy
|
|
308
|
+
// fetched later in the session cannot be used until the next launch (see
|
|
309
|
+
// widevine-cdm.ts). A cold cache is a no-op; ensureWidevineCdm fills it.
|
|
310
|
+
if (usingAntiDetectBrowser) {
|
|
311
|
+
const seeded = await seedWidevineCdm(userDataDir);
|
|
312
|
+
if (seeded) {
|
|
313
|
+
log.debug(`[AntiDetect] Widevine CDM ${seeded.version} seeded into the profile before launch`);
|
|
143
314
|
}
|
|
144
315
|
}
|
|
145
316
|
const browserArgs = [
|
|
@@ -154,17 +325,114 @@ export const launchBrowserWithNewProfile = async (executablePath, options = {})
|
|
|
154
325
|
'--no-default-browser-check',
|
|
155
326
|
];
|
|
156
327
|
// Add stealth arguments for anti-bot evasion. buildStealthArgs produces a
|
|
157
|
-
// platform-scoped set aligned with nodriver
|
|
328
|
+
// platform-scoped set aligned with nodriver - see the rationale block above
|
|
158
329
|
// the UNIVERSAL_STEALTH_ARGS constant for per-flag evidence.
|
|
330
|
+
// Chromium keeps only the LAST --enable-features switch on the command
|
|
331
|
+
// line, so two independent `push('--enable-features=...')` calls
|
|
332
|
+
// silently drop one another's features. Everything that wants a feature
|
|
333
|
+
// enabled adds it here and a single switch is emitted below.
|
|
334
|
+
const enabledFeatures = [];
|
|
159
335
|
if (enableStealthMode) {
|
|
160
336
|
browserArgs.push(...buildStealthArgs({ usingAntiDetectBrowser }));
|
|
161
337
|
}
|
|
162
338
|
if (dnsOverHttpsServer) {
|
|
163
|
-
|
|
339
|
+
enabledFeatures.push('UseDnsHttpsSvcb', 'SecureDns');
|
|
340
|
+
browserArgs.push(`--dns-over-https-mode=${secureDnsMode ?? 'secure'}`, `--dns-over-https-templates=${dnsOverHttpsServer}`);
|
|
164
341
|
}
|
|
165
342
|
if (webrtcIpHandlingPolicy) {
|
|
166
343
|
browserArgs.push(`--force-webrtc-ip-handling-policy=${webrtcIpHandlingPolicy}`);
|
|
167
344
|
}
|
|
345
|
+
if (options.fingerprintOptions?.forceEmulateWebgl === true) {
|
|
346
|
+
// Two switch names, because two binaries are in play and each reads
|
|
347
|
+
// only its own. Verified with `strings` on the shipped artifacts:
|
|
348
|
+
//
|
|
349
|
+
// dist/linux-x64/chrome (151) force-gpu-emulation x2
|
|
350
|
+
// dist/linux-x64.cromite-147.bak/… (147) force-webgl-emulation x2
|
|
351
|
+
//
|
|
352
|
+
// and neither name appears in the other binary. The 147 artifact is
|
|
353
|
+
// the one currently published to the KV store, so pushing only the
|
|
354
|
+
// 151 name means `forceEmulateWebgl: true` is silently dropped for
|
|
355
|
+
// every consumer on the released browser - exactly the way stock
|
|
356
|
+
// Chrome drops an unknown switch. Pushing only the 147 name has the
|
|
357
|
+
// mirror-image problem once #148 flips.
|
|
358
|
+
//
|
|
359
|
+
// Same dual-pass shape as --blink-settings-file /
|
|
360
|
+
// --blink-platform-overrides-file above. Delete the 147 line at the
|
|
361
|
+
// #148 flip, not before.
|
|
362
|
+
//
|
|
363
|
+
// The canonical declaration is switches::kForceGpuEmulation in
|
|
364
|
+
// chrome/common/chrome_switches.{h,cc}, propagated to
|
|
365
|
+
// renderer/GPU children via the kCommonSwitchNames list in
|
|
366
|
+
// chrome/browser/chrome_content_browser_client.cc. The renderer
|
|
367
|
+
// WebGL factory hook in third_party/blink/renderer/modules/webgl/
|
|
368
|
+
// webgl_rendering_context_base.cc reads the switch and, when
|
|
369
|
+
// FingerprintConfig::IsLoaded() also holds, returns the in-process
|
|
370
|
+
// EmulatedWebGraphicsContext3DProvider directly (skipping the GPU
|
|
371
|
+
// command-buffer round-trip entirely). On stock Chrome the switch
|
|
372
|
+
// is unknown and silently dropped.
|
|
373
|
+
browserArgs.push('--force-gpu-emulation');
|
|
374
|
+
browserArgs.push('--force-webgl-emulation');
|
|
375
|
+
// WebGPU has to answer for the same GPU the WebGL strings claim.
|
|
376
|
+
// Emulating WebGL leaves Dawn untouched, and on a GPU-less container
|
|
377
|
+
// `navigator.gpu.requestAdapter()` then resolves to NULL - so a
|
|
378
|
+
// session whose WEBGL_debug_renderer_info names an Apple M4 Pro also
|
|
379
|
+
// says, one line later, that the machine has no GPU at all. Measured
|
|
380
|
+
// in a Linux container against the real-macOS-Chrome baseline
|
|
381
|
+
// (test/docker-linux/baselines/coherence-macos-chrome151.json):
|
|
382
|
+
//
|
|
383
|
+
// without these switches hasAdapter=false vendor=null
|
|
384
|
+
// with them hasAdapter=true vendor=apple architecture=metal-3
|
|
385
|
+
//
|
|
386
|
+
// The two switches are upstream Chromium, not ours:
|
|
387
|
+
// --enable-unsafe-webgpu lifts the "no supported adapter" refusal
|
|
388
|
+
// that Linux applies to software adapters, and
|
|
389
|
+
// --use-webgpu-adapter=swiftshader points Dawn at the
|
|
390
|
+
// libvk_swiftshader.so that already ships in our dist. The adapter's
|
|
391
|
+
// IDENTITY still comes from us: the GPUAdapter info override in
|
|
392
|
+
// third_party/blink/renderer/modules/webgpu/gpu_adapter.cc reads
|
|
393
|
+
// vendor/architecture/device/description out of FingerprintConfig, so
|
|
394
|
+
// the software adapter reports the claimed Apple GPU rather than
|
|
395
|
+
// SwiftShader.
|
|
396
|
+
browserArgs.push('--enable-unsafe-webgpu');
|
|
397
|
+
browserArgs.push('--use-webgpu-adapter=swiftshader');
|
|
398
|
+
// …but --enable-unsafe-webgpu also turns on the experimental WebGPU
|
|
399
|
+
// surface, which installs `subgroupMatrixConfigs` on GPUAdapterInfo.
|
|
400
|
+
// Real macOS Chrome has no such property, so enumerating the info
|
|
401
|
+
// object's keys would show one the claimed browser cannot have -
|
|
402
|
+
// a switch tell earned while fixing a GPU tell. Turning the
|
|
403
|
+
// experimental features back off keeps the adapter and drops the key
|
|
404
|
+
// (verified in-container: infoKeys then match macOS exactly).
|
|
405
|
+
browserArgs.push('--disable-blink-features=WebGPUExperimentalFeatures');
|
|
406
|
+
// A laptop has a camera and a microphone. A container has neither, so
|
|
407
|
+
// `navigator.mediaDevices.enumerateDevices()` returns an EMPTY list
|
|
408
|
+
// while every other surface says MacBook - and the empty list needs
|
|
409
|
+
// no permission to read. The fake-device switch registers one
|
|
410
|
+
// synthetic camera, microphone and speaker, which is exactly the
|
|
411
|
+
// shape real macOS Chrome reports: 3 devices, kinds
|
|
412
|
+
// audioinput/audiooutput/videoinput, sharing one groupId (measured
|
|
413
|
+
// against Chrome 151 on macOS 26.5.2, and byte-identical in the
|
|
414
|
+
// container afterwards).
|
|
415
|
+
//
|
|
416
|
+
// Deliberately NOT --use-fake-ui-for-media-stream: that auto-accepts
|
|
417
|
+
// the permission prompt, and a camera permission that resolves
|
|
418
|
+
// without a user gesture is its own tell. Labels stay empty and
|
|
419
|
+
// permissions stay "prompt", same as an untouched profile.
|
|
420
|
+
browserArgs.push('--use-fake-device-for-media-stream');
|
|
421
|
+
// Scrollbars are drawn by the platform, and macOS draws them as
|
|
422
|
+
// OVERLAYS - zero layout width. GTK reserves 15 CSS px, which any
|
|
423
|
+
// page measures with two divs and no permission: a "Mac" whose
|
|
424
|
+
// scrollbars take 15px of layout is contradicting itself. Measured
|
|
425
|
+
// in-container: 15/15 without this switch, 0/0 with it, against 0/0
|
|
426
|
+
// on real Chrome 151/macOS.
|
|
427
|
+
enabledFeatures.push('OverlayScrollbar');
|
|
428
|
+
log.debug('[AntiDetect] forceEmulateWebgl=true → pushed --force-gpu-emulation + --force-webgl-emulation '
|
|
429
|
+
+ '+ --enable-unsafe-webgpu + --use-webgpu-adapter=swiftshader + --use-fake-device-for-media-stream');
|
|
430
|
+
}
|
|
431
|
+
// The one --enable-features switch, built from every contributor above.
|
|
432
|
+
if (enabledFeatures.length > 0) {
|
|
433
|
+
browserArgs.push(`--enable-features=${enabledFeatures.join(',')}`);
|
|
434
|
+
log.debug(`[AntiDetect] enabled features: ${enabledFeatures.join(',')}`);
|
|
435
|
+
}
|
|
168
436
|
// Set window size - either specific dimensions or maximize
|
|
169
437
|
// Don't maximize if specific dimensions are provided to avoid conflicts
|
|
170
438
|
if (windowWidth && windowHeight) {
|
|
@@ -184,41 +452,87 @@ export const launchBrowserWithNewProfile = async (executablePath, options = {})
|
|
|
184
452
|
browserArgs.push(`--disable-extensions-except=${extList}`, // optional, keeps Chrome from loading the rest
|
|
185
453
|
`--load-extension=${extList}`);
|
|
186
454
|
}
|
|
187
|
-
// Anti-detect browser: write
|
|
188
|
-
//
|
|
455
|
+
// Anti-detect browser: write platform-overrides JSON and pass its path to the
|
|
456
|
+
// browser via the config switch (see the two-name note below - the shipped
|
|
457
|
+
// 147 binary calls it --blink-platform-overrides-file, the vanilla-151 one
|
|
458
|
+
// --blink-settings-file). The C++ singleton reads this file at startup
|
|
459
|
+
// (lazy, first access); the browser process re-emits the content inline as
|
|
460
|
+
// the matching -data switch so sandboxed children (renderer/GPU/utility)
|
|
461
|
+
// can parse it without file IO.
|
|
189
462
|
if (usingAntiDetectBrowser) {
|
|
190
463
|
try {
|
|
191
464
|
// Prefer a pre-generated per-session config (passed by browser-plugin.ts) so that
|
|
192
465
|
// each browser instance gets unique C++-level hardware fingerprints (GPU, screen,
|
|
193
466
|
// CPU). Fall back to the static default_fingerprint.json if none was provided.
|
|
194
|
-
|
|
467
|
+
// Throws if neither source yields a config - see resolveAntiDetectFingerprintConfig.
|
|
468
|
+
const configToWrite = await resolveAntiDetectFingerprintConfig(options.fingerprintConfig, async () => {
|
|
195
469
|
const { default: def } = await import(
|
|
196
|
-
/* @vite-ignore */ new URL('../../browser/config/default_fingerprint.json', import.meta.url).href)
|
|
470
|
+
/* @vite-ignore */ new URL('../../browser/config/default_fingerprint.json', import.meta.url).href);
|
|
197
471
|
return def;
|
|
198
|
-
})
|
|
199
|
-
|
|
472
|
+
});
|
|
473
|
+
{
|
|
200
474
|
fingerprintConfigPath = await writeTempFingerprintConfig(configToWrite);
|
|
201
|
-
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
475
|
+
// Opaque switch name (renamed from --fingerprint-config). Mimics
|
|
476
|
+
// existing Blink runtime-flag shape so /proc/<pid>/cmdline does
|
|
477
|
+
// not advertise an anti-detect-specific flag.
|
|
478
|
+
// #145 lockstep: the vanilla-151 binary renamed this switch to
|
|
479
|
+
// --blink-settings-file (switches::kBlinkSettingsFile). It knows
|
|
480
|
+
// no other name, and because it hard-fails when neither
|
|
481
|
+
// --blink-settings-file nor --blink-settings-data is present
|
|
482
|
+
// (#78), sending only the old name does not degrade gracefully -
|
|
483
|
+
// the browser SIGTRAPs before the CDP endpoint is ready and the
|
|
484
|
+
// crawler cannot launch it at all. The SHIPPED Cromite/147
|
|
485
|
+
// artifact has the mirror-image problem: it only knows the old
|
|
486
|
+
// name and hard-fails the same way if it is not passed.
|
|
487
|
+
//
|
|
488
|
+
// TEMPORARY COMPATIBILITY SHIM, not design. Both names are
|
|
489
|
+
// pushed, pointing at the SAME file, so one crawler build drives
|
|
490
|
+
// both binaries. Each binary reads the name it knows and ignores
|
|
491
|
+
// the other: base::CommandLine::AppendSwitchesAndArguments
|
|
492
|
+
// (base/command_line.cc) stores every `--k=v` token in a plain
|
|
493
|
+
// map with no allowlist, so an unrecognised switch is inert -
|
|
494
|
+
// nothing in the tree validates the switch set, and the only
|
|
495
|
+
// switch-name-driven warning path (kBadFlags in
|
|
496
|
+
// chrome/browser/ui/startup/bad_flags_prompt.cc) is a fixed list
|
|
497
|
+
// that contains neither name. Verified against the artifacts
|
|
498
|
+
// themselves: `strings` on the 147 build exports only
|
|
499
|
+
// "blink-platform-overrides-file", on the 151 build only
|
|
500
|
+
// "blink-settings-file".
|
|
501
|
+
//
|
|
502
|
+
// DELETE the legacy line below - and this comment - once the
|
|
503
|
+
// shipped artifact is the vanilla line, i.e. after the browser
|
|
504
|
+
// submodule pointer is flipped to the 151 build (#148). At that
|
|
505
|
+
// point no binary we ship or download reads the old name, and
|
|
506
|
+
// keeping it only adds a needless token to /proc/<pid>/cmdline.
|
|
507
|
+
browserArgs.push(`--blink-settings-file=${fingerprintConfigPath}`);
|
|
508
|
+
// LEGACY (Cromite/147 only) - remove with #148.
|
|
509
|
+
browserArgs.push(`--blink-platform-overrides-file=${fingerprintConfigPath}`);
|
|
510
|
+
// Anti-Detect 2026-05-18: also pass the spoofed User-Agent via
|
|
511
|
+
// Chromium's standard `--user-agent` switch so the HTTP wire
|
|
512
|
+
// layer sees the same UA as JS-visible `navigator.userAgent`.
|
|
513
|
+
// The previous mechanism - reading FingerprintConfig directly
|
|
514
|
+
// from `net/url_request/url_request_http_job.cc` - was a
|
|
515
|
+
// net/→blink/ layering violation that broke component builds
|
|
516
|
+
// (FAST_BUILD=1). `--user-agent` is honored by
|
|
517
|
+
// embedder_support::GetUserAgent() which HttpUserAgentSettings
|
|
518
|
+
// caches per-NetworkContext, so every outgoing HTTP request
|
|
519
|
+
// carries the spoofed UA without any cross-layer dep.
|
|
520
|
+
// Accept-Language is already covered by the `--lang=` switch
|
|
521
|
+
// pushed below (line ~520) plus the `setenv("LANG"/"LC_ALL")`
|
|
522
|
+
// pin in chrome_main_delegate.cc::BasicStartupComplete.
|
|
523
|
+
const spoofedUA = configToWrite?.navigator?.userAgent;
|
|
524
|
+
if (spoofedUA) {
|
|
525
|
+
browserArgs.push(`--user-agent=${spoofedUA}`);
|
|
216
526
|
}
|
|
217
|
-
log.debug(`[
|
|
527
|
+
log.debug(`[Blink] Wrote platform-overrides config: ${fingerprintConfigPath}${options.fingerprintConfig ? ' (per-session)' : ' (static default)'}`);
|
|
218
528
|
}
|
|
219
529
|
}
|
|
220
530
|
catch (configErr) {
|
|
221
|
-
|
|
531
|
+
// Never swallow a config failure for the anti-detect binary: a
|
|
532
|
+
// browser launched without the platform-overrides config runs
|
|
533
|
+
// unspoofed and silently leaks the host identity. Surface it so
|
|
534
|
+
// the launch aborts rather than shipping a detectable session.
|
|
535
|
+
throw new Error(`[AntiDetect] Failed to prepare the fingerprint config for the anti-detect browser - refusing to launch unspoofed: ${configErr instanceof Error ? configErr.message : String(configErr)}`);
|
|
222
536
|
}
|
|
223
537
|
}
|
|
224
538
|
// --lang is understood by every Chrome binary (stock + anti-detect) and configures
|
|
@@ -235,14 +549,24 @@ export const launchBrowserWithNewProfile = async (executablePath, options = {})
|
|
|
235
549
|
browserArgs.push(`--lang=${langArg || options.resolvedLocale.locale}`);
|
|
236
550
|
}
|
|
237
551
|
else {
|
|
238
|
-
// No locale resolved
|
|
552
|
+
// No locale resolved - preserve historical default so we don't change behavior
|
|
239
553
|
// for callers that never touched fingerprintOptions.
|
|
240
554
|
browserArgs.push('--lang=en-US,en');
|
|
241
555
|
}
|
|
242
556
|
if (anonymizedProxyUrl) {
|
|
243
557
|
browserArgs.push(`--proxy-server=${anonymizedProxyUrl}`);
|
|
244
558
|
}
|
|
245
|
-
|
|
559
|
+
// Anti-Detect 2026-05-18: `--ignore-certificate-errors` is JS-detectable.
|
|
560
|
+
// A page can issue a `fetch()` to a sub-resource hosted under a
|
|
561
|
+
// self-signed certificate; real Chrome blocks the load (cert error),
|
|
562
|
+
// we silently succeed. That single asymmetry is enough to flag us.
|
|
563
|
+
//
|
|
564
|
+
// When running the native anti-detect binary we therefore force real
|
|
565
|
+
// certificate validation, regardless of the caller's `ignoreHttpsErrors`
|
|
566
|
+
// setting - the stealth invariant (mimic real Chrome wire behaviour)
|
|
567
|
+
// overrides the convenience flag. Stock-Chromium / non-stealth callers
|
|
568
|
+
// keep the historical default.
|
|
569
|
+
if (ignoreHttpsErrors && !usingAntiDetectBrowser) {
|
|
246
570
|
browserArgs.push('--ignore-certificate-errors');
|
|
247
571
|
// disabled because of cloudflare blocking
|
|
248
572
|
// browserArgs.push('--disable-web-security');
|
|
@@ -251,20 +575,35 @@ export const launchBrowserWithNewProfile = async (executablePath, options = {})
|
|
|
251
575
|
browserArgs.push('--headless=new');
|
|
252
576
|
}
|
|
253
577
|
log.debug(`Launching browser with user data dir: ${userDataDir}`);
|
|
254
|
-
const chromeVersion = getChromeBinaryVersion(executablePath);
|
|
255
|
-
if (chromeVersion) {
|
|
256
|
-
log.debug(`Using Chrome binary version: ${chromeVersion}`);
|
|
257
|
-
}
|
|
258
578
|
log.debug('browserArgs', browserArgs);
|
|
259
579
|
log.debug(`Stealth mode: ${enableStealthMode}, Profile populated: ${shouldPopulateProfile}, cleanup profile: ${shouldCleanupUserDataDir}`);
|
|
260
|
-
// Chrome bundles its own ICU so LANG/LC_ALL don't drive Intl
|
|
580
|
+
// Chrome bundles its own ICU so LANG/LC_ALL don't drive Intl - but a handful of
|
|
261
581
|
// system APIs (file dialogs, native notifications, extension readers) consult them.
|
|
262
582
|
// Setting these matches what a real user's shell would do and costs nothing.
|
|
583
|
+
//
|
|
584
|
+
// We also override TZ and LANGUAGE: a hardcoded host TZ (e.g. Europe/Berlin from
|
|
585
|
+
// an Apify Dockerfile) would otherwise leak through to anything that reads
|
|
586
|
+
// /etc/localtime or honors $TZ for fallback paths. Chrome's main Intl/Date path
|
|
587
|
+
// is covered by Emulation.setTimezoneOverride (called from fingerprint-injector)
|
|
588
|
+
// but workers and out-of-process subsystems can still consult $TZ.
|
|
263
589
|
const spawnEnv = { ...process.env };
|
|
264
590
|
if (options.resolvedLocale?.posix) {
|
|
265
591
|
spawnEnv.LANG = options.resolvedLocale.posix;
|
|
266
592
|
spawnEnv.LC_ALL = options.resolvedLocale.posix;
|
|
267
593
|
}
|
|
594
|
+
if (options.resolvedLocale?.languages) {
|
|
595
|
+
// GTK/glib and some apps read $LANGUAGE for fallback chains; align with the
|
|
596
|
+
// primary language (strip q-values).
|
|
597
|
+
const primaryLang = options.resolvedLocale.languages
|
|
598
|
+
.split(',')[0]
|
|
599
|
+
?.split(';')[0]
|
|
600
|
+
?.trim();
|
|
601
|
+
if (primaryLang)
|
|
602
|
+
spawnEnv.LANGUAGE = primaryLang;
|
|
603
|
+
}
|
|
604
|
+
if (options.resolvedLocale?.timezone) {
|
|
605
|
+
spawnEnv.TZ = options.resolvedLocale.timezone;
|
|
606
|
+
}
|
|
268
607
|
// Pipe stderr instead of swallowing it so a Chromium crash on Linux
|
|
269
608
|
// (missing shared lib, sandbox failure, GLIBC mismatch, …) surfaces
|
|
270
609
|
// in the logs instead of presenting as a generic "Failed to connect to
|
|
@@ -274,10 +613,27 @@ export const launchBrowserWithNewProfile = async (executablePath, options = {})
|
|
|
274
613
|
const browserProcess = spawn(executablePath, browserArgs, {
|
|
275
614
|
stdio: ['ignore', 'ignore', 'pipe'],
|
|
276
615
|
env: spawnEnv,
|
|
616
|
+
// Put the browser in its own process group (pgid === pid) so the
|
|
617
|
+
// whole Chromium process tree - parent + renderer/GPU/utility
|
|
618
|
+
// helpers - can be reaped at once via `process.kill(-pid, sig)`.
|
|
619
|
+
// Without this, signals hit only the parent and helpers orphan to
|
|
620
|
+
// launchd/init, leaking RAM and exhausting macOS IOSurface clients.
|
|
621
|
+
// Ctrl+C is still handled explicitly by signal-cleanup.ts, so losing
|
|
622
|
+
// the TTY's automatic SIGINT forwarding to the group is fine.
|
|
623
|
+
detached: true,
|
|
277
624
|
});
|
|
625
|
+
const proc = new BrowserProcess(browserProcess, userDataDir, shouldCleanupUserDataDir);
|
|
278
626
|
const stderrBuffer = [];
|
|
279
627
|
const STDERR_BUFFER_LIMIT = 50;
|
|
280
628
|
let stderrLeftover = '';
|
|
629
|
+
// Chromium writes a large volume of benign stderr on headless container
|
|
630
|
+
// launches (GPU-less init, ALSA "no sound card", dbus, WebRTC P2P
|
|
631
|
+
// resolve failures, the DevTools banner, zygote teardown races, …).
|
|
632
|
+
// None of it is actionable at runtime, so EVERY stderr line is logged at
|
|
633
|
+
// DEBUG only - it surfaces solely when the Apify log level is DEBUG.
|
|
634
|
+
// Real failures are not lost: the `exit` handler below dumps the buffered
|
|
635
|
+
// stderr tail at WARNING whenever Chromium exits with a non-zero code,
|
|
636
|
+
// which is the signature of an actual crash.
|
|
281
637
|
browserProcess.stderr?.on('data', (chunk) => {
|
|
282
638
|
stderrLeftover += chunk.toString('utf8');
|
|
283
639
|
const lines = stderrLeftover.split('\n');
|
|
@@ -289,26 +645,47 @@ export const launchBrowserWithNewProfile = async (executablePath, options = {})
|
|
|
289
645
|
if (stderrBuffer.length > STDERR_BUFFER_LIMIT)
|
|
290
646
|
stderrBuffer.shift();
|
|
291
647
|
log.debug(`[Chromium stderr] ${line}`);
|
|
648
|
+
// Puppeteer-style readiness: Chromium prints the browser-level CDP
|
|
649
|
+
// WebSocket endpoint here once its remote-debugging server is
|
|
650
|
+
// accepting connections. Parsing it is the exact moment the browser
|
|
651
|
+
// is connectable - no TCP-port poll or fixed pre-connect delay
|
|
652
|
+
// needed.
|
|
653
|
+
const wsMatch = line.match(/DevTools listening on (ws:\S+)/);
|
|
654
|
+
if (wsMatch) {
|
|
655
|
+
proc.resolveWsEndpoint(wsMatch[1]);
|
|
656
|
+
}
|
|
292
657
|
}
|
|
293
658
|
});
|
|
294
659
|
// Surface the buffered stderr if Chrome exits before something else
|
|
295
|
-
// gets a chance to inspect it (typical crash-on-startup signature)
|
|
660
|
+
// gets a chance to inspect it (typical crash-on-startup signature), and
|
|
661
|
+
// fail the readiness promise fast with that same tail so a dead binary
|
|
662
|
+
// never presents as a generic connect timeout.
|
|
296
663
|
browserProcess.once('exit', (code, signal) => {
|
|
664
|
+
if (stderrLeftover) {
|
|
665
|
+
stderrBuffer.push(stderrLeftover);
|
|
666
|
+
stderrLeftover = '';
|
|
667
|
+
}
|
|
668
|
+
const tail = stderrBuffer.slice(-20).join('\n');
|
|
297
669
|
if (code !== 0 && code !== null) {
|
|
298
|
-
if (stderrLeftover)
|
|
299
|
-
stderrBuffer.push(stderrLeftover);
|
|
300
|
-
const tail = stderrBuffer.slice(-20).join('\n');
|
|
301
670
|
log.warning(`Chromium exited with code=${code} signal=${signal}. `
|
|
302
|
-
+ `Last stderr lines:\n${tail || '(empty
|
|
671
|
+
+ `Last stderr lines:\n${tail || '(empty - Chrome wrote nothing to stderr)'}`);
|
|
303
672
|
}
|
|
673
|
+
// No-op once the ws line was already seen (settled-once guard).
|
|
674
|
+
proc.rejectWsEndpoint(new Error(`Chromium exited (code=${code} signal=${signal}) before the CDP WebSocket endpoint was ready. `
|
|
675
|
+
+ `Last stderr lines:\n${tail || '(empty - Chrome wrote nothing to stderr)'}`));
|
|
304
676
|
});
|
|
305
|
-
|
|
306
|
-
//
|
|
677
|
+
// A spawn-level failure (missing binary, EACCES, …) fires `error` and may
|
|
678
|
+
// never fire `exit`, so reject readiness here too. Also settled-once.
|
|
679
|
+
browserProcess.once('error', (err) => {
|
|
680
|
+
const tail = stderrBuffer.slice(-20).join('\n');
|
|
681
|
+
proc.rejectWsEndpoint(new Error(`Browser process failed to start before the CDP WebSocket endpoint was ready: ${err.message}${tail ? `\nLast stderr lines:\n${tail}` : ''}`));
|
|
682
|
+
});
|
|
683
|
+
// Track the temp fingerprint config file on the BrowserProcess so it is
|
|
684
|
+
// removed both on normal exit (via cleanup()) and on a SIGINT/SIGTERM
|
|
685
|
+
// signal (via the signal-cleanup registry), not just the exit event -
|
|
686
|
+
// which never fires when the process is interrupted before teardown.
|
|
307
687
|
if (fingerprintConfigPath) {
|
|
308
|
-
|
|
309
|
-
browserProcess.once('exit', () => {
|
|
310
|
-
fs.unlink(configPath).catch(() => { });
|
|
311
|
-
});
|
|
688
|
+
proc.trackFingerprintConfigPath(fingerprintConfigPath);
|
|
312
689
|
}
|
|
313
690
|
return proc;
|
|
314
691
|
}
|
|
@@ -317,7 +694,18 @@ export const launchBrowserWithNewProfile = async (executablePath, options = {})
|
|
|
317
694
|
throw new Error(`Error launching the browser: ${error}`);
|
|
318
695
|
}
|
|
319
696
|
};
|
|
320
|
-
|
|
697
|
+
// Cache the resolved writable base directory for the process lifetime. The
|
|
698
|
+
// mkdir + write-test probe below only depends on the filesystem layout and the
|
|
699
|
+
// process's cwd/home, none of which change underneath us mid-process, so the
|
|
700
|
+
// probe is run once and its result reused. Only a SUCCESSFUL probe is cached -
|
|
701
|
+
// a transient failure (e.g. a full disk during the first launch) must be
|
|
702
|
+
// retried on the next launch rather than pinned for the whole process. Each
|
|
703
|
+
// launch still gets its own fresh user-data-dir via fs.mkdtemp under the base.
|
|
704
|
+
let cachedBaseTempDir = null;
|
|
705
|
+
const resolveWritableBaseTempDir = async () => {
|
|
706
|
+
if (cachedBaseTempDir) {
|
|
707
|
+
return cachedBaseTempDir;
|
|
708
|
+
}
|
|
321
709
|
// Try to create temp directory in order of preference:
|
|
322
710
|
// 1. Project's temp directory (if we're in a project)
|
|
323
711
|
// 2. User's home directory temp folder
|
|
@@ -330,7 +718,6 @@ export const createTempUserDataDir = async () => {
|
|
|
330
718
|
// System temp directory (fallback)
|
|
331
719
|
path.join(os.tmpdir(), 'cdp-crawler-browsers'),
|
|
332
720
|
];
|
|
333
|
-
let baseTempDir = null;
|
|
334
721
|
for (const candidate of tempDirCandidates) {
|
|
335
722
|
try {
|
|
336
723
|
await fs.mkdir(candidate, { recursive: true });
|
|
@@ -338,17 +725,18 @@ export const createTempUserDataDir = async () => {
|
|
|
338
725
|
const testFile = path.join(candidate, '.write-test');
|
|
339
726
|
await fs.writeFile(testFile, 'test');
|
|
340
727
|
await fs.unlink(testFile);
|
|
341
|
-
|
|
342
|
-
|
|
728
|
+
cachedBaseTempDir = candidate;
|
|
729
|
+
return candidate;
|
|
343
730
|
}
|
|
344
731
|
catch (error) {
|
|
345
732
|
log.debug(`Cannot use temp directory ${candidate}: ${error}`);
|
|
346
733
|
continue;
|
|
347
734
|
}
|
|
348
735
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
736
|
+
throw new Error('Could not find a writable directory for browser profiles');
|
|
737
|
+
};
|
|
738
|
+
export const createTempUserDataDir = async () => {
|
|
739
|
+
const baseTempDir = await resolveWritableBaseTempDir();
|
|
352
740
|
const tempDirPrefix = path.join(baseTempDir, 'browser_');
|
|
353
741
|
const userDataDir = await fs.mkdtemp(tempDirPrefix);
|
|
354
742
|
log.debug(`Created browser profile directory: ${userDataDir}`);
|
|
@@ -359,7 +747,7 @@ export const launchChromeBrowserWithNewProfile = async (options = {}) => {
|
|
|
359
747
|
// CDPBrowserPlugin._launch() (resolveAntiDetectBrowserPath throws there if the
|
|
360
748
|
// binary is missing). Direct callers of this function without native stealth
|
|
361
749
|
// still fall through to the system Chrome lookup.
|
|
362
|
-
let executablePath = options
|
|
750
|
+
let { executablePath } = options;
|
|
363
751
|
if (!executablePath) {
|
|
364
752
|
const wantsNativeStealth = options.useNativeStealth || options.fingerprintOptions?.useNativeStealth;
|
|
365
753
|
if (wantsNativeStealth) {
|