fix: Chrome без менеджера паролей, перевода и автозаполнения (#111)
Перед каждым запуском окна в Default/Preferences профиля дописываются prefs: credentials_enable_service, profile.password_manager_enabled, translate.enabled, autofill.* = false. Применяется и к новым, и к существующим профилям, остальные настройки Chrome не трогаются. Пузырь «Сохранить пароль?» и предложение перевода больше не появляются, пароли не оседают в Login Data профиля. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
677fea0090
commit
232385f8d8
3 files changed
+74
No files matched your search
@@ -13,6 +13,7 @@ navigator.userAgentData и движком, и Cloudflare такой браузе
|
|||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import random
|
import random
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -24,6 +25,16 @@ SETTINGS_FILE = "robotex_session.json"
|
|||||||
# окно браузера под Xvfb — 1920x1080, монитор физически не может быть меньше окна
|
# окно браузера под Xvfb — 1920x1080, монитор физически не может быть меньше окна
|
||||||
MIN_SCREEN = (1920, 1080)
|
MIN_SCREEN = (1920, 1080)
|
||||||
CANVAS_NOISE_JS = (Path(__file__).parent / "js" / "canvas_noise.js").read_text()
|
CANVAS_NOISE_JS = (Path(__file__).parent / "js" / "canvas_noise.js").read_text()
|
||||||
|
# prefs Chrome, которые задаются профилю при каждом запуске (#111): без пузыря «Сохранить пароль?»,
|
||||||
|
# предложения перевода и автозаполнения — секреты клиентов не оседают в Login Data/Web Data профиля.
|
||||||
|
# --disable-translate в современных Chrome не действует, поэтому через prefs. На JS-отпечаток не влияют.
|
||||||
|
CHROME_PREFS = {
|
||||||
|
"credentials_enable_service": False,
|
||||||
|
"credentials_enable_autosignin": False,
|
||||||
|
"profile": {"password_manager_enabled": False, "password_manager_leak_detection": False},
|
||||||
|
"translate": {"enabled": False},
|
||||||
|
"autofill": {"enabled": False, "profile_enabled": False, "credit_card_enabled": False},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def new_canvas_seed() -> int:
|
def new_canvas_seed() -> int:
|
||||||
@@ -81,3 +92,28 @@ def browser_kwargs(settings: dict) -> dict:
|
|||||||
"additional_args": {"screen": fp["screen"], "device_scale_factor": fp["device_scale_factor"]},
|
"additional_args": {"screen": fp["screen"], "device_scale_factor": fp["device_scale_factor"]},
|
||||||
"init_script_content": CANVAS_NOISE_JS.replace("__SEED__", str(int(fp["canvas_seed"]))),
|
"init_script_content": CANVAS_NOISE_JS.replace("__SEED__", str(int(fp["canvas_seed"]))),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _merge(target: dict, source: dict) -> dict:
|
||||||
|
for key, value in source.items():
|
||||||
|
if isinstance(value, dict) and isinstance(target.get(key), dict):
|
||||||
|
_merge(target[key], value)
|
||||||
|
else:
|
||||||
|
target[key] = value
|
||||||
|
return target
|
||||||
|
|
||||||
|
|
||||||
|
def apply_chrome_prefs(user_data_dir: Path) -> None:
|
||||||
|
"""Дописать CHROME_PREFS в Default/Preferences профиля, не трогая остальное. Вызывается до запуска
|
||||||
|
Chrome: и для нового профиля, и для существующего — Chrome при выходе сохраняет эти prefs как есть."""
|
||||||
|
path = user_data_dir / "Default" / "Preferences"
|
||||||
|
try:
|
||||||
|
prefs = json.loads(path.read_text())
|
||||||
|
if not isinstance(prefs, dict):
|
||||||
|
prefs = {}
|
||||||
|
except (OSError, json.JSONDecodeError):
|
||||||
|
prefs = {}
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
tmp = path.with_name(path.name + ".robotex")
|
||||||
|
tmp.write_text(json.dumps(_merge(prefs, CHROME_PREFS), ensure_ascii=False))
|
||||||
|
os.replace(tmp, path)
|
||||||
@@ -349,6 +349,8 @@ class WindowPool:
|
|||||||
touch_profile(user_data_dir)
|
touch_profile(user_data_dir)
|
||||||
# отпечаток задаётся профилю один раз (см. api/profile.py)
|
# отпечаток задаётся профилю один раз (см. api/profile.py)
|
||||||
profile_kwargs = profile.browser_kwargs(profile.load_or_create(user_data_dir))
|
profile_kwargs = profile.browser_kwargs(profile.load_or_create(user_data_dir))
|
||||||
|
# без менеджера паролей, перевода и автозаполнения (#111)
|
||||||
|
profile.apply_chrome_prefs(user_data_dir)
|
||||||
|
|
||||||
proxy = parse_proxy(payload.proxy) if payload.proxy else None
|
proxy = parse_proxy(payload.proxy) if payload.proxy else None
|
||||||
# geo — по exit-IP прокси (см. api/geoip.py); без прокси оставляем настройки браузера
|
# geo — по exit-IP прокси (см. api/geoip.py); без прокси оставляем настройки браузера
|
||||||
|
|||||||
@@ -69,3 +69,39 @@ def test_profiles_get_different_canvas_seeds(tmp_path):
|
|||||||
(tmp_path / str(i)).mkdir()
|
(tmp_path / str(i)).mkdir()
|
||||||
seeds.add(profile.load_or_create(tmp_path / str(i))["fingerprint"]["canvas_seed"])
|
seeds.add(profile.load_or_create(tmp_path / str(i))["fingerprint"]["canvas_seed"])
|
||||||
assert len(seeds) == 20
|
assert len(seeds) == 20
|
||||||
|
|
||||||
|
|
||||||
|
def test_chrome_prefs_for_new_profile(tmp_path):
|
||||||
|
profile.apply_chrome_prefs(tmp_path)
|
||||||
|
prefs = json.loads((tmp_path / "Default" / "Preferences").read_text())
|
||||||
|
assert prefs["credentials_enable_service"] is False
|
||||||
|
assert prefs["profile"]["password_manager_enabled"] is False
|
||||||
|
assert prefs["translate"]["enabled"] is False
|
||||||
|
assert prefs["autofill"]["credit_card_enabled"] is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_chrome_prefs_keep_existing_profile_settings(tmp_path):
|
||||||
|
"""Существующий профиль: prefs дописываются, остальные настройки Chrome остаются."""
|
||||||
|
path = tmp_path / "Default" / "Preferences"
|
||||||
|
path.parent.mkdir()
|
||||||
|
path.write_text(json.dumps({
|
||||||
|
"profile": {"name": "Person 1", "password_manager_enabled": True},
|
||||||
|
"translate": {"enabled": True},
|
||||||
|
"autofill": {"autofill_ai": {"opt_in_status": {}}},
|
||||||
|
"intl": {"accept_languages": "ru-RU,ru"},
|
||||||
|
}))
|
||||||
|
profile.apply_chrome_prefs(tmp_path)
|
||||||
|
prefs = json.loads(path.read_text())
|
||||||
|
assert prefs["profile"] == {"name": "Person 1", "password_manager_enabled": False, "password_manager_leak_detection": False}
|
||||||
|
assert prefs["translate"]["enabled"] is False
|
||||||
|
assert prefs["autofill"]["autofill_ai"] == {"opt_in_status": {}}
|
||||||
|
assert prefs["intl"] == {"accept_languages": "ru-RU,ru"}
|
||||||
|
assert prefs["credentials_enable_service"] is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_chrome_prefs_replace_broken_preferences(tmp_path):
|
||||||
|
path = tmp_path / "Default" / "Preferences"
|
||||||
|
path.parent.mkdir()
|
||||||
|
path.write_text("{broken")
|
||||||
|
profile.apply_chrome_prefs(tmp_path)
|
||||||
|
assert json.loads(path.read_text())["translate"] == {"enabled": False}
|
||||||
Reference in new issue
Block a user