s
This commit is contained in:
@@ -113,7 +113,6 @@ def render(count: int) -> str:
|
||||
' - "./external-extensions:/usr/share/chromium/extensions:ro"',
|
||||
' - "./external-extensions:/usr/share/chromium-browser/extensions:ro"',
|
||||
' - "./tampermonkey-seed:/etc/neko/tampermonkey-seed:ro"',
|
||||
' - "./youtube-auto-dismiss-extension:/etc/neko/youtube-auto-dismiss-extension:ro"',
|
||||
' - "./provisioning:/var/www/provisioning:ro"',
|
||||
' - "./browser-launch.sh:/etc/neko/browser-launch.sh:ro"',
|
||||
f" - {q(f'./{supervisor_conf}:/etc/neko/supervisord/{supervisor_conf}:ro')}",
|
||||
|
||||
@@ -333,6 +333,70 @@ def verify_neko(checks: list[dict[str, Any]], env: dict[str, str], *, timeout: f
|
||||
detail=(out or err2).strip(),
|
||||
hint="Make services/neko/browser-launch.sh executable and restart Neko.",
|
||||
)
|
||||
code, out, err2 = docker_exec(
|
||||
name,
|
||||
"if [ \"${NEKO_MEMBER_PROVIDER:-}\" = multiuser ] "
|
||||
"&& [ -n \"${NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD:-}\" ] "
|
||||
"&& [ -n \"${NEKO_MEMBER_MULTIUSER_USER_PASSWORD:-}\" ]; then "
|
||||
"printf 'provider=%s admin_password=set user_password=set\\n' \"$NEKO_MEMBER_PROVIDER\"; "
|
||||
"else echo 'NEKO member env invalid'; exit 1; fi",
|
||||
timeout=timeout,
|
||||
)
|
||||
check(
|
||||
checks,
|
||||
f"neko_{idx}_multiuser_admin",
|
||||
f"{name} multiuser admin enabled",
|
||||
code == 0,
|
||||
detail=(out or err2).strip(),
|
||||
hint="Neko must run with multiuser provider and a non-empty admin password.",
|
||||
)
|
||||
code, out, err2 = docker_exec(
|
||||
name,
|
||||
"awk '$5==\"/home/neko/.config/google-chrome\" || $5==\"/home/neko/.config/chromium\" {print $5}' "
|
||||
"/proc/self/mountinfo | sort -u",
|
||||
timeout=timeout,
|
||||
)
|
||||
mounted_profiles = {line.strip() for line in out.splitlines() if line.strip()}
|
||||
check(
|
||||
checks,
|
||||
f"neko_{idx}_persistent_profile",
|
||||
f"{name} persistent browser profile",
|
||||
code == 0
|
||||
and {
|
||||
"/home/neko/.config/google-chrome",
|
||||
"/home/neko/.config/chromium",
|
||||
}.issubset(mounted_profiles),
|
||||
detail=out.strip() or err2.strip() or "profile mounts missing",
|
||||
hint="Neko profile directories must be bind-mounted so Google login, Tampermonkey, and channel state survive restarts.",
|
||||
)
|
||||
code, out, err2 = docker_exec(
|
||||
name,
|
||||
"python3 - <<'PY'\n"
|
||||
"import json\n"
|
||||
"from pathlib import Path\n"
|
||||
"for path in (Path('/home/neko/.config/google-chrome/Default/Preferences'), Path('/home/neko/.config/chromium/Default/Preferences'), Path('/home/neko/.config/google-chrome/Local State'), Path('/home/neko/.config/chromium/Local State')):\n"
|
||||
" if not path.exists():\n"
|
||||
" continue\n"
|
||||
" try:\n"
|
||||
" data = json.loads(path.read_text(encoding='utf-8'))\n"
|
||||
" except Exception:\n"
|
||||
" continue\n"
|
||||
" if data.get('extensions', {}).get('ui', {}).get('developer_mode') is True:\n"
|
||||
" print(f'{path}: developer_mode=true')\n"
|
||||
" raise SystemExit(0)\n"
|
||||
"print('Developer mode preference not found')\n"
|
||||
"raise SystemExit(1)\n"
|
||||
"PY",
|
||||
timeout=timeout,
|
||||
)
|
||||
check(
|
||||
checks,
|
||||
f"neko_{idx}_developer_mode",
|
||||
f"{name} Chrome developer mode",
|
||||
code == 0,
|
||||
detail=(out or err2).strip(),
|
||||
hint="browser-launch.sh must set extensions.ui.developer_mode in Preferences/Local State before Chrome starts.",
|
||||
)
|
||||
code, out, err2 = docker_exec(
|
||||
name,
|
||||
"find /etc/chromium/policies/managed /etc/chromium-browser/policies/managed /etc/opt/chrome/policies/managed "
|
||||
@@ -379,22 +443,89 @@ def verify_neko(checks: list[dict[str, Any]], env: dict[str, str], *, timeout: f
|
||||
detail="payload contains YouTube Studio Auto Dismiss" if code == 0 else (err2 or out).strip(),
|
||||
hint="Regenerate services/neko/provisioning/tampermonkey-provisioning.json and restart Neko.",
|
||||
)
|
||||
code, out, err2 = docker_exec(
|
||||
name,
|
||||
"python3 - <<'PY'\n"
|
||||
"import json\n"
|
||||
"from pathlib import Path\n"
|
||||
"ext = 'dhdgffkkebhmkfjojejmpbldmpobfkfo'\n"
|
||||
"for base in ('/home/neko/.config/google-chrome/Default', '/home/neko/.config/chromium/Default'):\n"
|
||||
" path = Path(base) / 'Preferences'\n"
|
||||
" if not path.exists():\n"
|
||||
" continue\n"
|
||||
" try:\n"
|
||||
" data = json.loads(path.read_text(encoding='utf-8'))\n"
|
||||
" except Exception:\n"
|
||||
" continue\n"
|
||||
" state = data.get('extensions', {}).get('settings', {}).get(ext, {})\n"
|
||||
" if state:\n"
|
||||
" path_value = str(state.get('path') or '')\n"
|
||||
" enabled = (\n"
|
||||
" state.get('state') == 1\n"
|
||||
" and not state.get('disable_reasons')\n"
|
||||
" and state.get('from_webstore') is True\n"
|
||||
" and not path_value.startswith('/')\n"
|
||||
" and state.get('location') != 8\n"
|
||||
" )\n"
|
||||
" print(\n"
|
||||
" f\"{base}: state={state.get('state')} disable_reasons={state.get('disable_reasons')} \"\n"
|
||||
" f\"from_webstore={state.get('from_webstore')} location={state.get('location')} path={path_value}\"\n"
|
||||
" )\n"
|
||||
" raise SystemExit(0 if enabled else 1)\n"
|
||||
"print('Tampermonkey preference not found')\n"
|
||||
"raise SystemExit(1)\n"
|
||||
"PY",
|
||||
timeout=timeout,
|
||||
)
|
||||
check(
|
||||
checks,
|
||||
f"neko_{idx}_tm_enabled",
|
||||
f"{name} Tampermonkey enabled",
|
||||
code == 0,
|
||||
detail=(out or err2).strip(),
|
||||
hint="Tampermonkey must be a normal enabled extension, not an unpacked forced-disabled extension.",
|
||||
)
|
||||
code, out, err2 = docker_exec(
|
||||
name,
|
||||
"python3 - <<'PY'\n"
|
||||
"import os\n"
|
||||
"skip = {os.getpid(), os.getppid()}\n"
|
||||
"bad = [b'--load-' + b'extension=', b'youtube-auto-' + b'dismiss-extension']\n"
|
||||
"for name in os.listdir('/proc'):\n"
|
||||
" if not name.isdigit() or int(name) in skip:\n"
|
||||
" continue\n"
|
||||
" try:\n"
|
||||
" cmdline = open(f'/proc/{name}/cmdline', 'rb').read()\n"
|
||||
" except OSError:\n"
|
||||
" continue\n"
|
||||
" if any(marker in cmdline for marker in bad):\n"
|
||||
" print(cmdline.replace(b'\\0', b' ').decode('utf-8', errors='replace'))\n"
|
||||
" raise SystemExit(1)\n"
|
||||
"print('no standalone youtube extension')\n"
|
||||
"PY",
|
||||
timeout=timeout,
|
||||
)
|
||||
check(
|
||||
checks,
|
||||
f"neko_{idx}_no_standalone_youtube_extension",
|
||||
f"{name} no standalone YouTube extension",
|
||||
code == 0,
|
||||
detail=(out or err2).strip(),
|
||||
hint="The YouTube Studio Auto Dismiss automation must live inside Tampermonkey, not as a separate --load-extension.",
|
||||
)
|
||||
code, out, err2 = docker_exec(
|
||||
name,
|
||||
"for base in /home/neko/.config/google-chrome/Default /home/neko/.config/chromium/Default; do "
|
||||
"for area in 'Local Extension Settings' 'Managed Extension Settings'; do "
|
||||
"dir=\"$base/$area/dhdgffkkebhmkfjojejmpbldmpobfkfo\"; "
|
||||
"if [ -d \"$dir\" ] && grep -R -a -l 'YouTube Studio Auto Dismiss' \"$dir\" 2>/dev/null | head -1; then exit 0; fi; "
|
||||
"done; done; "
|
||||
"for cmd in /proc/[0-9]*/cmdline; do "
|
||||
"tr '\\0' '\\n' <\"$cmd\" 2>/dev/null | grep -q -- '--load-extension=.*youtube-auto-dismiss-extension' && echo '--load-extension youtube-auto-dismiss-extension' && exit 0; "
|
||||
"done; exit 1",
|
||||
"done; done; exit 1",
|
||||
timeout=timeout,
|
||||
)
|
||||
check(
|
||||
checks,
|
||||
f"neko_{idx}_tm_storage",
|
||||
f"{name} Tampermonkey script imported",
|
||||
f"{name} Tampermonkey YouTube script imported",
|
||||
code == 0 and bool(out.strip()),
|
||||
required=True,
|
||||
detail=out.strip() or err2.strip() or "not found",
|
||||
|
||||
Reference in New Issue
Block a user