#!/bin/sh set -eu ROOT="$(cd "$(dirname "$0")/.." && pwd)" TMP_DIR="$(mktemp -d)" trap 'rm -rf "$TMP_DIR"' EXIT INT TERM export HOME="$TMP_DIR/home" export LEMANA_VPN_BIN_DIR="$HOME/bin" export LEMANA_VPN_CONFIG_DIR="$HOME/.config/lemana-vpn" export OPENCONNECT_LITE_CONFIG_DIR="$HOME/.config/openconnect-lite" mkdir -p "$HOME" # Every connection below uses fake clients; cleanup and status must stay isolated too. safe_bin="$TMP_DIR/safe-bin" mkdir -p "$safe_bin" printf '#!/bin/sh\ncase "$*" in *" --recover") printf "no_snapshot\\n" ;; *" --begin "*) printf "ready\\n" ;; *" --recover-session "*) printf "cancelled\\n" ;; esac\nexit 0\n' > "$safe_bin/sudo" printf '#!/bin/sh\nexit 0\n' > "$safe_bin/networksetup" printf '#!/bin/sh\nexit 1\n' > "$safe_bin/security" chmod +x "$safe_bin/sudo" "$safe_bin/networksetup" "$safe_bin/security" export PATH="$safe_bin:$PATH" export LEMANA_VPN_CONNECT_WAIT_SECONDS=0.1 printf '#!/bin/sh\n# LEMANA_DNS_PROTOCOL=2\n# LEMANA_SESSION_PROTOCOL=1\n# LEMANA_RECONNECT_PROTOCOL=1\nprintf "no_snapshot\\n"\n' > "$TMP_DIR/fake-dns-cleanup" chmod +x "$TMP_DIR/fake-dns-cleanup" export LEMANA_VPN_DNS_CLEANUP="$TMP_DIR/fake-dns-cleanup" output="$(cd "$ROOT" && sh install.sh --dry-run --non-interactive --minimal)" printf '%s\n' "$output" | grep -q 'Detected state:' printf '%s\n' "$output" | grep -q 'Interactive prompts: off' printf '%s\n' "$output" | grep -q 'Modules: mode=manual-keychain bitwarden=0 touchid=0 sudoers=1 shell=1 app=1 autostart=1' printf '%s\n' "$output" | grep -q 'Проверяю Homebrew-зависимости' printf '%s\n' "$output" | grep -q 'Swift build может занять минуту' printf '%s\n' "$output" | grep -q 'sudo install -m 755 -o root -g wheel .* /usr/local/sbin/lemana-vpn-dns-cleanup' printf '%s\n' "$output" | grep -q 'swift build -c release --package-path' printf '%s\n' "$output" | grep -q 'launchctl load' printf '%s\n' "$output" | grep -q 'restart LemanaVPN.app if running' printf '%s\n' "$output" | grep -q '+ cp .*/bin/vpn-lemanapro.sh ' printf '%s\n' "$output" | grep -q '+ cp .*/bin/lemana-vpn-import-yandex.py ' if printf '%s\n' "$output" | grep -q 'curl -fsSL .*raw/branch/main'; then echo "checkout install dry-run should use local files, not remote main" >&2 exit 1 fi import_plan="$(sh "$ROOT/install.sh" --dry-run --non-interactive --import-yandex-key --without-app --no-shell --no-sudoers)" printf '%s\n' "$import_plan" | grep -q -- 'vpn-lemanapro.sh --configure-login' printf '%s\n' "$import_plan" | grep -q -- 'vpn-lemanapro.sh --import-yandex-key' if sh "$ROOT/install.sh" --dry-run --non-interactive --mode bitwarden-auto --import-yandex-key >/dev/null 2>&1; then echo 'installer must reject mixing Bitwarden mode with local OTP import' >&2 exit 1 fi esc="$(printf '\033')" if printf '%s\n' "$output" | grep -q "$esc"; then echo "non-tty dry-run output contains ANSI color codes" >&2 exit 1 fi if command -v expect >/dev/null 2>&1; then interactive_output="$( ROOT="$ROOT" expect <<'EXPECT' set timeout 30 spawn sh $env(ROOT)/install.sh --dry-run --interactive --without-app --no-shell --no-sudoers expect "Какой режим использовать?" expect "Выбор" send "\r" expect "Импортировать код из Яндекс Ключа" send "n\r" expect eof EXPECT )" printf '%s\n' "$interactive_output" | grep -q 'Есть два режима' printf '%s\n' "$interactive_output" | grep -q 'Какой режим использовать?' printf '%s\n' "$interactive_output" | grep -q '1) Ручной вход: запомнить логин и пароль' printf '%s\n' "$interactive_output" | grep -q '2) Автоматический вход' printf '%s\n' "$interactive_output" | grep -q 'Выбран режим: ручной вход: логин и пароль в Keychain, одноразовый код вручную' printf '%s\n' "$interactive_output" | grep -q '+ .*/vpn-lemanapro.sh --configure-login' if printf '%s\n' "$interactive_output" | grep -Eq 'credential_source|auth_profile|saved-totp|TOTP seed'; then echo "basic interactive wizard should not expose technical credential wording" >&2 exit 1 fi fi status_json="$(bash "$ROOT/bin/vpn-lemanapro.sh" --status --json)" printf '%s\n' "$status_json" | grep -q '"modules":' printf '%s\n' "$status_json" | grep -q '"app":' printf '%s\n' "$status_json" | grep -q '"mode":{"id":"manual-keychain"' printf '%s\n' "$status_json" | grep -q '"app_menu":{"install_label":"Режим: ручной вход, логин и пароль не настроены","configured_mode":"manual-keychain","available_modes":\["configured","bitwarden-auto"\],"setup_hint":"Настройте в Terminal: vpn --configure-login"}' keychain_status_json="$(LEMANA_VPN_CREDENTIAL_SOURCE=keychain bash "$ROOT/bin/vpn-lemanapro.sh" --status --json)" printf '%s\n' "$keychain_status_json" | grep -q '"mode":{"id":"manual-keychain"' printf '%s\n' "$keychain_status_json" | grep -q '"credentials":{"source":"keychain","auth_profile":"saved-login","login_ready":false,"keychain_ready":false}' printf '%s\n' "$keychain_status_json" | grep -q '"bitwarden":{"enabled":false' printf '%s\n' "$keychain_status_json" | grep -q '"touchid":{"enabled":false' bitwarden_status_json="$(LEMANA_VPN_CREDENTIAL_SOURCE=bitwarden bash "$ROOT/bin/vpn-lemanapro.sh" --status --json)" printf '%s\n' "$bitwarden_status_json" | grep -q '"mode":{"id":"bitwarden-auto"' printf '%s\n' "$bitwarden_status_json" | grep -q '"credentials":{"source":"bitwarden","auth_profile":"auto","login_ready":false,"keychain_ready":false}' printf '%s\n' "$bitwarden_status_json" | grep -q '"bitwarden":{"enabled":true' status_password_bin="$TMP_DIR/status-password-bin" mkdir -p "$status_password_bin" cat > "$status_password_bin/security" <<'SH' #!/bin/sh if [ "${1:-}" = "find-generic-password" ]; then account="" while [ "$#" -gt 0 ]; do if [ "$1" = "-a" ]; then shift account="${1:-}" break fi shift done case "$account" in ""|totp/*) exit 1 ;; *) exit 0 ;; esac fi exit 1 SH chmod +x "$status_password_bin/security" status_full_bin="$TMP_DIR/status-full-bin" mkdir -p "$status_full_bin" cat > "$status_full_bin/security" <<'SH' #!/bin/sh if [ "${1:-}" = "find-generic-password" ]; then account="" while [ "$#" -gt 0 ]; do if [ "$1" = "-a" ]; then shift account="${1:-}" break fi shift done [ -n "$account" ] exit $? fi exit 1 SH chmod +x "$status_full_bin/security" manual_keychain_missing_status="$( LEMANA_VPN_USERNAME="lemana-status-login-missing" \ LEMANA_VPN_MODE=manual-keychain \ bash "$ROOT/bin/vpn-lemanapro.sh" --status --json )" printf '%s\n' "$manual_keychain_missing_status" | grep -q '"mode":{"id":"manual-keychain","label":"Вручную","ready":false' printf '%s\n' "$manual_keychain_missing_status" | grep -q '"app_menu":{"install_label":"Режим: ручной вход, логин и пароль не настроены","configured_mode":"manual-keychain","available_modes":\["configured","bitwarden-auto"\],"setup_hint":"Настройте в Terminal: vpn --configure-login"}' manual_keychain_ready_status="$( PATH="$status_password_bin:$PATH" \ LEMANA_VPN_USERNAME="lemana-status-login-ready" \ LEMANA_VPN_MODE=manual-keychain \ bash "$ROOT/bin/vpn-lemanapro.sh" --status --json )" printf '%s\n' "$manual_keychain_ready_status" | grep -q '"mode":{"id":"manual-keychain","label":"Вручную","ready":true' printf '%s\n' "$manual_keychain_ready_status" | grep -q '"app_menu":{"install_label":"Режим: ручной вход, логин и пароль сохранены","configured_mode":"manual-keychain","available_modes":\["configured","bitwarden-auto"\],"setup_hint":null}' legacy_saved_totp_status="$( LEMANA_VPN_USERNAME="lemana-status-legacy-saved-totp" \ LEMANA_VPN_AUTH_PROFILE=saved-totp \ LEMANA_VPN_CREDENTIAL_SOURCE=keychain \ bash "$ROOT/bin/vpn-lemanapro.sh" --status --json )" printf '%s\n' "$legacy_saved_totp_status" | grep -q '"mode":{"id":"manual-keychain"' bitwarden_auto_ready_status="$( PATH="$status_full_bin:$PATH" \ LEMANA_VPN_USERNAME="lemana-status-bitwarden-ready" \ LEMANA_VPN_MODE=bitwarden-auto \ bash "$ROOT/bin/vpn-lemanapro.sh" --status --json )" printf '%s\n' "$bitwarden_auto_ready_status" | grep -q '"mode":{"id":"bitwarden-auto"' printf '%s\n' "$bitwarden_auto_ready_status" | grep -q '"app_menu":{"install_label":"Автоматически · Bitwarden","configured_mode":"bitwarden-auto","available_modes":\["configured","manual-keychain"\],"setup_hint":null}' auto_ready_status="$( PATH="$status_full_bin:$PATH" \ LEMANA_VPN_USERNAME="lemana-status-auto-ready" \ LEMANA_VPN_AUTH_PROFILE=auto \ LEMANA_VPN_CREDENTIAL_SOURCE=keychain \ bash "$ROOT/bin/vpn-lemanapro.sh" --status --json )" printf '%s\n' "$auto_ready_status" | grep -q '"mode":{"id":"bitwarden-auto"' personal_ldap='601''03293' if grep -R "$personal_ldap" "$ROOT/bin" "$ROOT/install.sh" "$ROOT/uninstall.sh" "$ROOT/README.md" "$ROOT/templates" "$ROOT/app" "$ROOT/.agents" >/dev/null; then echo "personal LDAP username must not be hardcoded in runtime, installer, app, docs, or agent guidance" >&2 exit 1 fi grep -q 'LemanaVPN-openconnect-lite.log' "$ROOT/bin/vpn-lemanapro.sh" grep -q '"event":"waiting"' "$ROOT/bin/vpn-lemanapro.sh" grep -q -- '--patch-only' "$ROOT/bin/vpn-lemanapro.sh" grep -q -- '--auto' "$ROOT/bin/vpn-lemanapro.sh" grep -q -- '--manual' "$ROOT/bin/vpn-lemanapro.sh" grep -q -- '--manual-full' "$ROOT/bin/vpn-lemanapro.sh" grep -q -- '--manual-sso' "$ROOT/bin/vpn-lemanapro.sh" grep -q 'LEMANA_VPN_AUTOFILL_DISABLE' "$ROOT/bin/vpn-lemanapro.sh" grep -q 'LEMANA_VPN_AUTOFILL_CLICK' "$ROOT/bin/vpn-lemanapro.sh" grep -q 'LEMANA_VPN_AUTOFILL_TOTP' "$ROOT/bin/vpn-lemanapro.sh" grep -q 'vpn-auto' "$ROOT/install.sh" grep -q 'vpn-manual' "$ROOT/install.sh" grep -q 'vpn-manual-full' "$ROOT/install.sh" grep -q 'vpnManager.connect()' "$ROOT/app/Sources/LemanaVPN/LemanaVPNApp.swift" if grep -q 'vpnManager.connect(mode:' "$ROOT/app/Sources/LemanaVPN/LemanaVPNApp.swift"; then echo 'Connect must use saved configuration, not a launch override' >&2 exit 1 fi grep -q 'vpnManager.setAuth(' "$ROOT/app/Sources/LemanaVPN/LemanaVPNApp.swift" grep -q 'vpnManager.installModeLabel' "$ROOT/app/Sources/LemanaVPN/LemanaVPNApp.swift" grep -q 'vpnManager.installSetupHint' "$ROOT/app/Sources/LemanaVPN/LemanaVPNApp.swift" grep -q 'menuButton("Логин и пароль…"' "$ROOT/app/Sources/LemanaVPN/LemanaVPNApp.swift" grep -q 'vpnManager.configureLogin()' "$ROOT/app/Sources/LemanaVPN/LemanaVPNApp.swift" grep -q 'vpnManager.importYandexKey()' "$ROOT/app/Sources/LemanaVPN/LemanaVPNApp.swift" grep -q 'menuBarExtraStyle(.window)' "$ROOT/app/Sources/LemanaVPN/LemanaVPNApp.swift" grep -q 'AppStorage("menuBarIndicatorStyle")' "$ROOT/app/Sources/LemanaVPN/LemanaVPNApp.swift" printf '%s\n' "$output" | grep -q 'app/Sources/LemanaVPN/SessionTiming.swift' if grep -Fq 'connect(mode: .auto)' "$ROOT/app/Sources/LemanaVPN/LemanaVPNApp.swift"; then echo "disconnected app menu should not hardcode the auto button" >&2 exit 1 fi if grep -Fq 'Button("Подставить только логин и пароль")' "$ROOT/app/Sources/LemanaVPN/LemanaVPNApp.swift"; then echo "disconnected app menu should not hardcode saved-login button visibility" >&2 exit 1 fi grep -q 'app_menu' "$ROOT/app/Sources/LemanaVPN/VPNManager.swift" grep -q 'available_modes' "$ROOT/app/Sources/LemanaVPN/VPNManager.swift" grep -q 'case "mode", "auth_profile":' "$ROOT/app/Sources/LemanaVPN/VPNManager.swift" grep -q 'case "manual_sso", "auto_sso":' "$ROOT/app/Sources/LemanaVPN/VPNManager.swift" grep -q 'case "username_required", "username_saved":' "$ROOT/app/Sources/LemanaVPN/VPNManager.swift" grep -q 'func configureLogin()' "$ROOT/app/Sources/LemanaVPN/VPNManager.swift" grep -q -- '--configure-login' "$ROOT/app/Sources/LemanaVPN/VPNManager.swift" grep -q 'enum VPNLaunchMode' "$ROOT/app/Sources/LemanaVPN/VPNManager.swift" grep -q 'struct Credentials: Decodable' "$ROOT/app/Sources/LemanaVPN/VPNManager.swift" grep -q 'credential_source' "$ROOT/app/Sources/LemanaVPN/VPNManager.swift" fake_webengine="$TMP_DIR/webengine_process.py" fake_authenticator="$TMP_DIR/authenticator.py" cat > "$fake_webengine" <<'PY' import json import sys class Browser: def __init__(self, profile): page = QWebEnginePage(profile, self) def run(self, display_mode, credentials, url_pattern, rules): argv = sys.argv.copy() if display_mode == "hidden": argv += ["-platform", "minimal"] if credentials: logger.info("Initiating autologin", cred=credentials) for url_pattern, rules in auto_fill_rules.items(): script = QWebEngineScript() script.setInjectionPoint(QWebEngineScript.InjectionPoint.DocumentReady) script.setWorldId(QWebEngineScript.ScriptWorldId.ApplicationWorld) script.setSourceCode( f""" // ==UserScript== // @include {url_pattern} // ==/UserScript== function autoFill() {{ {get_selectors(rules, credentials)} setTimeout(autoFill, 1000); }} autoFill(); """ ) self.page().scripts().insert(script) def get_selectors(rules, credentials): statements = [] for rule in rules: selector = json.dumps(rule.selector) if rule.action == "stop": statements.append( f"""var elem = document.querySelector({selector}); if (elem) {{ return; }}""" ) elif rule.fill: value = json.dumps(getattr(credentials, rule.fill, None)) if value: statements.append( f"""var elem = document.querySelector({selector}); if (elem) {{ elem.dispatchEvent(new Event("focus")); elem.value = {value}; elem.dispatchEvent(new Event("blur")); }}""" ) else: logger.warning( "Credential info not available", type=rule.fill, possibilities=dir(credentials), ) elif rule.action == "click": statements.append( f"""var elem = document.querySelector({selector}); if (elem) {{ elem.dispatchEvent(new Event("focus")); elem.click(); }}""" ) return "\n".join(statements) PY cat > "$fake_authenticator" <<'PY' import requests import structlog logger = structlog.get_logger() class Authenticator: def _detect_authentication_target_url(self): # Follow possible redirects in a GET request # Authentication will occur using a POST request on the final URL response = requests.get(self.host.vpn_url) response.raise_for_status() self.host.address = response.url logger.debug("Auth target url", url=self.host.vpn_url) def _start_authentication(self): pass PY LEMANA_VPN_WEBENGINE_PROCESS="$fake_webengine" \ LEMANA_VPN_AUTHENTICATOR="$fake_authenticator" \ LEMANA_VPN_OC_PYTHON=python3 \ LEMANA_VPN_PATCH_BACKUP_DIR="$TMP_DIR/patch-backups" \ bash "$ROOT/bin/vpn-lemanapro.sh" --patch-only >/dev/null grep -q '"offscreen"' "$fake_webengine" grep -q 'LEMANA_VPN_AUTOFILL_DISABLE' "$fake_webengine" grep -q 'new RegExp' "$fake_webengine" grep -q 'script.setWorldId(QWebEngineScript.ScriptWorldId.ApplicationWorld)' "$fake_webengine" grep -q 'new Event("input", {{bubbles: true}})' "$fake_webengine" grep -q 'LEMANA_VPN_AUTOFILL_CLICK' "$fake_webengine" grep -q 'os.environ.get("LEMANA_VPN_AUTOFILL_CLICK", "1") != "0"' "$fake_webengine" grep -q 'LEMANA_VPN_AUTOFILL_TOTP' "$fake_webengine" grep -q 'rule.fill == "totp"' "$fake_webengine" if grep -q 'ScriptWorldId.MainWorld' "$fake_webengine"; then echo "patched auto-fill should keep the original ApplicationWorld behavior" >&2 exit 1 fi if grep -q '__lemanaVpnClicked' "$fake_webengine"; then echo "obsolete click flag must not replace the bounded per-attempt guard" >&2 exit 1 fi if grep -q 'valueSetter' "$fake_webengine"; then echo "patched auto-fill should use the original direct value assignment with input/change events" >&2 exit 1 fi grep -q 'from urllib.parse import urljoin' "$fake_authenticator" grep -q 'self.session.get(self.host.vpn_url, allow_redirects=False)' "$fake_authenticator" grep -q 'response.headers.get("Location")' "$fake_authenticator" if grep -q 'requests.get(self.host.vpn_url)' "$fake_authenticator"; then echo "auth target detection must not follow redirects with bare requests.get" >&2 exit 1 fi status_text="$(bash "$ROOT/bin/vpn-lemanapro.sh" --status)" printf '%s\n' "$status_text" | grep -q 'Modules:' printf '%s\n' "$status_text" | grep -q 'core=' printf '%s\n' "$status_text" | grep -q 'app=' printf '%s\n' "$status_text" | grep -q 'autostart=' uninstall_home="$TMP_DIR/uninstall-home" mkdir -p "$uninstall_home" uninstall_output="$( HOME="$uninstall_home" \ LEMANA_VPN_BIN_DIR="$uninstall_home/bin" \ LEMANA_VPN_CONFIG_DIR="$uninstall_home/.config/lemana-vpn" \ OPENCONNECT_LITE_CONFIG_DIR="$uninstall_home/.config/openconnect-lite" \ sh "$ROOT/uninstall.sh" --dry-run --remove-keychain --remove-touchid-helper --remove-openconnect-lite )" printf '%s\n' "$uninstall_output" | grep -q 'Начинаю удаление Lemana VPN' printf '%s\n' "$uninstall_output" | grep -q 'Проверяю runtime-патчи openconnect-lite' printf '%s\n' "$uninstall_output" | grep -q 'Удаляю sudoers и DNS cleanup wrapper' printf '%s\n' "$uninstall_output" | grep -q 'killall LemanaVPN # if running' printf '%s\n' "$uninstall_output" | grep -q 'Удаляю VPN-записи из macOS Keychain' printf '%s\n' "$uninstall_output" | grep -q 'rm -f .*lemana-vpn-import-yandex.py' if printf '%s\n' "$uninstall_output" | grep -q "$esc"; then echo "non-tty uninstall dry-run output contains ANSI color codes" >&2 exit 1 fi missing_user="lemana-smoke-missing-$$" set +e manual_output="$( HOME="$HOME" \ LEMANA_VPN_USERNAME="$missing_user" \ LEMANA_VPN_AUTH_PROFILE=saved-login \ LEMANA_VPN_CREDENTIAL_SOURCE=keychain \ bash "$ROOT/bin/vpn-lemanapro.sh" --json 2>&1 )" manual_code=$? set -e [ "$manual_code" -ne 0 ] printf '%s\n' "$manual_output" | grep -q '"event":"keychain_required"' printf '%s\n' "$manual_output" | grep -q 'vpn --configure-login' if printf '%s\n' "$manual_output" | grep -q 'Cleaning up VPN DNS'; then echo "missing saved login should fail before VPN cleanup trap is installed" >&2 exit 1 fi missing_username_home="$TMP_DIR/missing-username-home" mkdir -p "$missing_username_home" set +e missing_username_output="$( HOME="$missing_username_home" \ LEMANA_VPN_CONFIG_DIR="$missing_username_home/.config/lemana-vpn" \ OPENCONNECT_LITE_CONFIG_DIR="$missing_username_home/.config/openconnect-lite" \ LEMANA_VPN_AUTH_PROFILE=saved-login \ LEMANA_VPN_CREDENTIAL_SOURCE=keychain \ bash "$ROOT/bin/vpn-lemanapro.sh" --json 2>&1 )" missing_username_code=$? set -e [ "$missing_username_code" -ne 0 ] printf '%s\n' "$missing_username_output" | grep -q '"event":"username_required"' printf '%s\n' "$missing_username_output" | grep -q 'LDAP username is missing' if printf '%s\n' "$missing_username_output" | grep -q '"event":"keychain_required"'; then echo "missing username should fail before checking Keychain credentials" >&2 exit 1 fi grep -q '^_tty_available() {' "$ROOT/bin/vpn-lemanapro.sh" if ! sed -n '/^_prompt_read()/,/^}/p' "$ROOT/bin/vpn-lemanapro.sh" | grep -q 'if _tty_available; then'; then echo "runtime prompt reads must use the robust /dev/tty availability probe" >&2 exit 1 fi preserve_username_home="$TMP_DIR/preserve-username-home" mkdir -p "$preserve_username_home/.config/lemana-vpn" "$preserve_username_home/.config/openconnect-lite" printf 'LEMANA_VPN_USERNAME="lemana-preserved-user"\nLEMANA_VPN_MODE="bitwarden-auto"\n' > "$preserve_username_home/.config/lemana-vpn/env" printf 'username = "lemana-preserved-user"\n' > "$preserve_username_home/.config/openconnect-lite/config.toml" preserve_username_output="$( cd "$ROOT" && HOME="$preserve_username_home" \ LEMANA_VPN_CONFIG_DIR="$preserve_username_home/.config/lemana-vpn" \ OPENCONNECT_LITE_CONFIG_DIR="$preserve_username_home/.config/openconnect-lite" \ sh install.sh --dry-run --non-interactive --mode bitwarden-auto --without-app --no-shell --no-sudoers )" printf '%s\n' "$preserve_username_output" | grep -q 'Preserving existing LDAP username from previous config' fake_path_bin="$TMP_DIR/fake-path-bin" mkdir -p "$fake_path_bin" cat > "$fake_path_bin/security" <<'SH' #!/bin/sh if [ "${1:-}" = "find-generic-password" ]; then account="" while [ "$#" -gt 0 ]; do if [ "$1" = "-a" ]; then shift account="${1:-}" break fi shift done [ -n "$account" ] exit $? fi exit 1 SH chmod +x "$fake_path_bin/security" fake_oc_bin="$TMP_DIR/fake-openconnect-lite" manual_full_capture="$TMP_DIR/manual-full-capture" cat > "$fake_oc_bin" <<'SH' #!/bin/sh { printf 'args=%s\n' "$*" for arg in "$@"; do printf 'arg=%s\n' "$arg" done printf 'autofill_disable=%s\n' "${LEMANA_VPN_AUTOFILL_DISABLE:-}" printf 'autofill_click=%s\n' "${LEMANA_VPN_AUTOFILL_CLICK:-}" printf 'autofill_totp=%s\n' "${LEMANA_VPN_AUTOFILL_TOTP:-}" config_home="${XDG_CONFIG_HOME:-$HOME/.config}" config_file="$config_home/openconnect-lite/config.toml" printf 'xdg_config_home=%s\n' "$config_home" if [ -f "$config_file" ]; then if grep -q '^\[credentials\]' "$config_file"; then printf 'credentials_block=1\n' else printf 'credentials_block=0\n' fi if grep -q 'username = ""' "$config_file"; then printf 'empty_username=1\n' else printf 'empty_username=0\n' fi else printf 'credentials_block=missing\n' printf 'empty_username=missing\n' fi } > "$LEMANA_VPN_CAPTURE_LAUNCH" exit 0 SH chmod +x "$fake_oc_bin" fake_missing_keychain_bin="$TMP_DIR/fake-missing-keychain-bin" mkdir -p "$fake_missing_keychain_bin" cat > "$fake_missing_keychain_bin/security" <<'SH' #!/bin/sh exit 1 SH chmod +x "$fake_missing_keychain_bin/security" manual_keychain_missing_home="$TMP_DIR/manual-keychain-missing-home" manual_keychain_missing_capture="$TMP_DIR/manual-keychain-missing-capture" mkdir -p "$manual_keychain_missing_home" mkdir -p "$manual_keychain_missing_home/.config/openconnect-lite" cat > "$manual_keychain_missing_home/.config/openconnect-lite/config.toml" <<'TOML' on_disconnect = "true" [default_profile] address = "vpn.lemanapro.ru" user_group = "" name = "Default-K" [credentials] username = "" TOML set +e manual_keychain_missing_output="$( HOME="$manual_keychain_missing_home" \ PATH="$fake_missing_keychain_bin:$PATH" \ LEMANA_VPN_CONFIG_DIR="$manual_keychain_missing_home/.config/lemana-vpn" \ OPENCONNECT_LITE_CONFIG_DIR="$manual_keychain_missing_home/.config/openconnect-lite" \ LEMANA_VPN_USERNAME="lemana-missing-login-$$" \ LEMANA_VPN_MODE=manual-keychain \ LEMANA_VPN_OC_BIN="$fake_oc_bin" \ LEMANA_VPN_OC_PYTHON=python3 \ LEMANA_VPN_WEBENGINE_PROCESS="$fake_webengine" \ LEMANA_VPN_AUTHENTICATOR="$fake_authenticator" \ LEMANA_VPN_PATCH_BACKUP_DIR="$TMP_DIR/manual-keychain-missing-patch-backups" \ LEMANA_VPN_DNS_CLEANUP="$TMP_DIR/fake-dns-cleanup" \ LEMANA_VPN_CONNECT_LOG="$TMP_DIR/manual-keychain-missing.log" \ LEMANA_VPN_CAPTURE_LAUNCH="$manual_keychain_missing_capture" \ bash "$ROOT/bin/vpn-lemanapro.sh" --json 2>&1 )" manual_keychain_missing_code=$? set -e [ "$manual_keychain_missing_code" -ne 0 ] printf '%s\n' "$manual_keychain_missing_output" | grep -q '"event":"keychain_required"' printf '%s\n' "$manual_keychain_missing_output" | grep -q 'vpn --configure-login' if [ -f "$manual_keychain_missing_capture" ]; then echo "manual-keychain without saved password should fail before launching openconnect-lite" >&2 exit 1 fi fake_login_keychain_bin="$TMP_DIR/fake-login-keychain-bin" mkdir -p "$fake_login_keychain_bin" cat > "$fake_login_keychain_bin/security" <<'SH' #!/bin/sh if [ "${1:-}" = "find-generic-password" ]; then account="" while [ "$#" -gt 0 ]; do if [ "$1" = "-a" ]; then shift account="${1:-}" break fi shift done case "$account" in ""|totp/*) exit 1 ;; *) exit 0 ;; esac fi exit 1 SH chmod +x "$fake_login_keychain_bin/security" manual_keychain_capture="$TMP_DIR/manual-keychain-capture" manual_keychain_output="$( HOME="$HOME" \ PATH="$fake_login_keychain_bin:$PATH" \ LEMANA_VPN_USERNAME="lemana-manual-keychain-$$" \ LEMANA_VPN_MODE=manual-keychain \ LEMANA_VPN_OC_BIN="$fake_oc_bin" \ LEMANA_VPN_OC_PYTHON=python3 \ LEMANA_VPN_WEBENGINE_PROCESS="$fake_webengine" \ LEMANA_VPN_AUTHENTICATOR="$fake_authenticator" \ LEMANA_VPN_PATCH_BACKUP_DIR="$TMP_DIR/manual-keychain-patch-backups" \ LEMANA_VPN_DNS_CLEANUP="$TMP_DIR/fake-dns-cleanup" \ LEMANA_VPN_CONNECT_LOG="$TMP_DIR/manual-keychain.log" \ LEMANA_VPN_CAPTURE_LAUNCH="$manual_keychain_capture" \ bash "$ROOT/bin/vpn-lemanapro.sh" --json )" printf '%s\n' "$manual_keychain_output" | grep -q '"event":"manual_sso","mode":"manual-keychain","autofill":true,"totp":false,"submit":false' if printf '%s\n' "$manual_keychain_output" | grep -q '"event":"keychain_required"'; then echo "manual-keychain should not require a TOTP seed" >&2 exit 1 fi grep -q -- '--browser-display-mode shown' "$manual_keychain_capture" grep -q '^autofill_disable=0$' "$manual_keychain_capture" grep -q '^autofill_click=0$' "$manual_keychain_capture" grep -q '^autofill_totp=0$' "$manual_keychain_capture" # Legacy local OTP migrates to automatic local login; explicit manual modes override it. for local_case in local manual full missing; do local_capture="$TMP_DIR/otp-$local_case.capture" local_path="$fake_path_bin:$PATH" local_arg="--json" [ "$local_case" != manual ] || local_arg="--manual" [ "$local_case" != full ] || local_arg="--manual-full" [ "$local_case" != missing ] || local_path="$fake_login_keychain_bin:$PATH" set +e local_output="$( PATH="$local_path" \ LEMANA_VPN_USERNAME="lemana-local-otp" \ LEMANA_VPN_MODE=manual-keychain \ LEMANA_VPN_LOCAL_OTP=1 \ LEMANA_VPN_OC_BIN="$fake_oc_bin" \ LEMANA_VPN_OC_PYTHON=python3 \ LEMANA_VPN_WEBENGINE_PROCESS="$fake_webengine" \ LEMANA_VPN_AUTHENTICATOR="$fake_authenticator" \ LEMANA_VPN_PATCH_BACKUP_DIR="$TMP_DIR/local-otp-patch-backups" \ LEMANA_VPN_DNS_CLEANUP="$TMP_DIR/fake-dns-cleanup" \ LEMANA_VPN_CONNECT_LOG="$TMP_DIR/otp-$local_case.log" \ LEMANA_VPN_CAPTURE_LAUNCH="$local_capture" \ bash "$ROOT/bin/vpn-lemanapro.sh" "$local_arg" --json )" local_code=$? set -e if [ "$local_case" = missing ]; then [ "$local_code" -ne 0 ] [ ! -f "$local_capture" ] printf '%s\n' "$local_output" | grep -q 'vpn --import-yandex-key' else [ "$local_code" -eq 0 ] if [ "$local_case" = local ]; then grep -q -- '--browser-display-mode hidden' "$local_capture" grep -q '^autofill_click=1$' "$local_capture" grep -q '^autofill_totp=1$' "$local_capture" printf '%s\n' "$local_output" | grep -q '"event":"auto_sso","mode":"keychain-auto","autofill":true,"totp":true,"submit":true' else grep -q -- '--browser-display-mode shown' "$local_capture" grep -q '^autofill_click=0$' "$local_capture" grep -q '^autofill_totp=0$' "$local_capture" if [ "$local_case" = full ]; then grep -q '^autofill_disable=1$' "$local_capture" else grep -q '^autofill_disable=0$' "$local_capture" fi fi fi done local_status="$(PATH="$fake_login_keychain_bin:$PATH" LEMANA_VPN_USERNAME=lemana-local-otp LEMANA_VPN_LOCAL_OTP=1 bash "$ROOT/bin/vpn-lemanapro.sh" --status --json)" printf '%s\n' "$local_status" | python3 -c 'import json,sys; m=json.load(sys.stdin)["modules"]; assert m["local_otp"] and not m["mode"]["ready"]; assert "--import-yandex-key" in m["mode"]["setup_hint"]' fake_bw_sync_bin="$TMP_DIR/fake-bw-sync-bin" mkdir -p "$fake_bw_sync_bin" cat > "$fake_bw_sync_bin/bw" <<'SH' #!/bin/sh case "${1:-}:${2:-}" in unlock:*) printf 'fake-session\n' ;; get:password) printf 'ldap-password-from-bitwarden\n' ;; get:item) printf '{"login":{"username":"lemana-bw-user","totp":"otpauth://totp/Lemana:test?secret=abcd2345efgh6723&issuer=Lemana"}}\n' ;; *) exit 1 ;; esac SH cat > "$fake_bw_sync_bin/security" <<'SH' #!/bin/sh if [ "${1:-}" = "find-generic-password" ]; then account="" while [ "$#" -gt 0 ]; do if [ "$1" = "-a" ]; then shift account="${1:-}" break fi shift done case "$account" in lemana-bw-user|totp/lemana-bw-user) exit 0 ;; *) exit 1 ;; esac fi exit 0 SH cat > "$fake_bw_sync_bin/keychain-fingerprint" <<'SH' #!/bin/sh if [ "${1:-}" = "get" ]; then printf 'fake-bitwarden-master-password\n' exit 0 fi exit 1 SH fake_store_python="$fake_bw_sync_bin/fake-oc-python" cat > "$fake_store_python" <<'SH' #!/bin/sh if [ "$#" -eq 1 ] && [ "${1:-}" = "-" ] && [ -n "${LEMANA_VPN_CAPTURE_STORE:-}" ]; then cat >/dev/null { printf 'username=%s\n' "${KC_USERNAME:-}" printf 'password=%s\n' "${_VPN_PASS:-}" printf 'totp=%s\n' "${_VPN_TOTP_SECRET:-}" } > "$LEMANA_VPN_CAPTURE_STORE" exit 0 fi exec python3 "$@" SH chmod +x "$fake_bw_sync_bin/bw" "$fake_bw_sync_bin/security" "$fake_bw_sync_bin/keychain-fingerprint" "$fake_store_python" bitwarden_username_home="$TMP_DIR/bitwarden-username-home" bitwarden_username_capture="$TMP_DIR/bitwarden-username-capture" bitwarden_store_capture="$TMP_DIR/bitwarden-store-capture" mkdir -p "$bitwarden_username_home/.config/lemana-vpn" "$bitwarden_username_home/.config/openconnect-lite" printf 'LEMANA_VPN_MODE="bitwarden-auto"\nLEMANA_VPN_USE_BITWARDEN="1"\nLEMANA_VPN_USE_TOUCHID="1"\n' > "$bitwarden_username_home/.config/lemana-vpn/env" cat > "$bitwarden_username_home/.config/openconnect-lite/config.toml" <<'TOML' on_disconnect = "true" [default_profile] address = "vpn.lemanapro.ru" user_group = "" name = "Default-K" [credentials] username = "" TOML bitwarden_username_output="$( HOME="$bitwarden_username_home" \ PATH="$fake_bw_sync_bin:$PATH" \ LEMANA_VPN_CONFIG_DIR="$bitwarden_username_home/.config/lemana-vpn" \ OPENCONNECT_LITE_CONFIG_DIR="$bitwarden_username_home/.config/openconnect-lite" \ LEMANA_VPN_KEYCHAIN_FINGERPRINT="$fake_bw_sync_bin/keychain-fingerprint" \ LEMANA_VPN_OC_BIN="$fake_oc_bin" \ LEMANA_VPN_OC_PYTHON="$fake_store_python" \ LEMANA_VPN_WEBENGINE_PROCESS="$fake_webengine" \ LEMANA_VPN_AUTHENTICATOR="$fake_authenticator" \ LEMANA_VPN_PATCH_BACKUP_DIR="$TMP_DIR/bitwarden-username-patch-backups" \ LEMANA_VPN_DNS_CLEANUP="$TMP_DIR/fake-dns-cleanup" \ LEMANA_VPN_CONNECT_LOG="$TMP_DIR/bitwarden-username.log" \ LEMANA_VPN_CAPTURE_LAUNCH="$bitwarden_username_capture" \ LEMANA_VPN_CAPTURE_STORE="$bitwarden_store_capture" \ bash "$ROOT/bin/vpn-lemanapro.sh" --json )" printf '%s\n' "$bitwarden_username_output" | grep -q '"event":"bw_touchid"' printf '%s\n' "$bitwarden_username_output" | grep -q '"event":"username_saved","source":"bitwarden"' printf '%s\n' "$bitwarden_username_output" | grep -q '"event":"auto_sso","mode":"bitwarden-auto","autofill":true,"totp":true,"submit":true' if printf '%s\n' "$bitwarden_username_output" | grep -q '"event":"username_required"'; then echo "bitwarden-auto must read LDAP username from Bitwarden instead of prompting for it" >&2 exit 1 fi grep -q '^username=lemana-bw-user$' "$bitwarden_store_capture" grep -q '^password=ldap-password-from-bitwarden$' "$bitwarden_store_capture" grep -q '^totp=ABCD2345EFGH6723$' "$bitwarden_store_capture" grep -q 'LEMANA_VPN_USERNAME="lemana-bw-user"' "$bitwarden_username_home/.config/lemana-vpn/env" grep -q 'username = "lemana-bw-user"' "$bitwarden_username_home/.config/openconnect-lite/config.toml" grep -q -- '--browser-display-mode hidden' "$bitwarden_username_capture" bitwarden_auto_capture="$TMP_DIR/bitwarden-auto-capture" bitwarden_auto_output="$( HOME="$HOME" \ PATH="$fake_path_bin:$PATH" \ LEMANA_VPN_USERNAME="lemana-bitwarden-auto-$$" \ LEMANA_VPN_MODE=bitwarden-auto \ LEMANA_VPN_LOCAL_OTP=1 \ LEMANA_VPN_USE_TOUCHID=0 \ LEMANA_VPN_OC_BIN="$fake_oc_bin" \ LEMANA_VPN_OC_PYTHON=python3 \ LEMANA_VPN_WEBENGINE_PROCESS="$fake_webengine" \ LEMANA_VPN_AUTHENTICATOR="$fake_authenticator" \ LEMANA_VPN_PATCH_BACKUP_DIR="$TMP_DIR/bitwarden-auto-patch-backups" \ LEMANA_VPN_DNS_CLEANUP="$TMP_DIR/fake-dns-cleanup" \ LEMANA_VPN_CONNECT_LOG="$TMP_DIR/bitwarden-auto.log" \ LEMANA_VPN_CAPTURE_LAUNCH="$bitwarden_auto_capture" \ bash "$ROOT/bin/vpn-lemanapro.sh" --json )" printf '%s\n' "$bitwarden_auto_output" | grep -q '"event":"auto_sso","mode":"bitwarden-auto","autofill":true,"totp":true,"submit":true' grep -q -- '--browser-display-mode hidden' "$bitwarden_auto_capture" grep -q '^arg=--script$' "$bitwarden_auto_capture" grep -Fq "arg=$TMP_DIR/fake-dns-cleanup --vpnc" "$bitwarden_auto_capture" grep -q '^autofill_disable=0$' "$bitwarden_auto_capture" grep -q '^autofill_click=1$' "$bitwarden_auto_capture" grep -q '^autofill_totp=1$' "$bitwarden_auto_capture" # A normal Connect has no mode argument and must use the last saved selection. profile_bin="$TMP_DIR/profile-bin" mkdir -p "$profile_bin" printf '#!/bin/sh\nprintf '\''{"status":"locked"}\\n'\''\n' > "$profile_bin/bw" chmod +x "$profile_bin/bw" profile_home="$TMP_DIR/profiles" mkdir -p "$profile_home/config" printf 'LEMANA_VPN_USERNAME="fixture-profile"\nLEMANA_VPN_USE_TOUCHID="0"\n' > "$profile_home/config/env" profile_cli() { HOME="$profile_home" PATH="$profile_bin:$fake_path_bin:$PATH" \ LEMANA_VPN_CONFIG_DIR="$profile_home/config" \ LEMANA_VPN_OC_BIN="$fake_oc_bin" LEMANA_VPN_OC_PYTHON=python3 \ LEMANA_VPN_WEBENGINE_PROCESS="$fake_webengine" LEMANA_VPN_AUTHENTICATOR="$fake_authenticator" \ LEMANA_VPN_PATCH_BACKUP_DIR="$profile_home/patches" \ LEMANA_VPN_DNS_CLEANUP="$TMP_DIR/fake-dns-cleanup" \ LEMANA_VPN_CONNECT_LOG="$profile_home/connect.log" \ LEMANA_VPN_CAPTURE_LAUNCH="$profile_home/launch" \ bash "$ROOT/bin/vpn-lemanapro.sh" "$@" } for profile in manual keychain bitwarden; do profile_cli "--set-auth=$profile" >/dev/null profile_cli --json > "$profile_home/events" if [ "$profile" = manual ]; then grep -q -- '--browser-display-mode shown' "$profile_home/launch" grep -q '^autofill_totp=0$' "$profile_home/launch" grep -q '^autofill_click=0$' "$profile_home/launch" else grep -q -- '--browser-display-mode hidden' "$profile_home/launch" grep -q '^autofill_totp=1$' "$profile_home/launch" grep -q '^autofill_click=1$' "$profile_home/launch" grep -q '"event":"auto_sso"' "$profile_home/events" fi done manual_full_no_credentials_home="$TMP_DIR/manual-full-no-credentials-home" manual_full_no_credentials_capture="$TMP_DIR/manual-full-no-credentials-capture" mkdir -p "$manual_full_no_credentials_home" mkdir -p "$manual_full_no_credentials_home/.config/openconnect-lite" cat > "$manual_full_no_credentials_home/.config/openconnect-lite/config.toml" <<'TOML' on_disconnect = "true" [default_profile] address = "vpn.lemanapro.ru" user_group = "" name = "Default-K" [credentials] username = "" TOML manual_full_no_credentials_output="$( HOME="$manual_full_no_credentials_home" \ PATH="$fake_missing_keychain_bin:$PATH" \ LEMANA_VPN_CONFIG_DIR="$manual_full_no_credentials_home/.config/lemana-vpn" \ OPENCONNECT_LITE_CONFIG_DIR="$manual_full_no_credentials_home/.config/openconnect-lite" \ LEMANA_VPN_CREDENTIAL_SOURCE=keychain \ LEMANA_VPN_OC_BIN="$fake_oc_bin" \ LEMANA_VPN_OC_PYTHON=python3 \ LEMANA_VPN_WEBENGINE_PROCESS="$fake_webengine" \ LEMANA_VPN_AUTHENTICATOR="$fake_authenticator" \ LEMANA_VPN_PATCH_BACKUP_DIR="$TMP_DIR/manual-full-no-credentials-patch-backups" \ LEMANA_VPN_DNS_CLEANUP="$TMP_DIR/fake-dns-cleanup" \ LEMANA_VPN_CONNECT_LOG="$TMP_DIR/manual-full-no-credentials.log" \ LEMANA_VPN_CAPTURE_LAUNCH="$manual_full_no_credentials_capture" \ bash "$ROOT/bin/vpn-lemanapro.sh" --manual-full --json )" printf '%s\n' "$manual_full_no_credentials_output" | grep -q '"event":"manual_sso","mode":"manual-keychain","diagnostic":true,"autofill":false,"totp":false,"submit":false' if printf '%s\n' "$manual_full_no_credentials_output" | grep -Eq '"event":"(username_required|keychain_required)"'; then echo "manual-full should not require saved username or Keychain credentials before opening the browser" >&2 exit 1 fi grep -q -- '--browser-display-mode shown' "$manual_full_no_credentials_capture" grep -q '^autofill_disable=1$' "$manual_full_no_credentials_capture" grep -q '^autofill_click=0$' "$manual_full_no_credentials_capture" grep -q '^autofill_totp=0$' "$manual_full_no_credentials_capture" grep -q '^credentials_block=0$' "$manual_full_no_credentials_capture" grep -q '^empty_username=0$' "$manual_full_no_credentials_capture" if command -v expect >/dev/null 2>&1; then first_username_home="$TMP_DIR/first-username-home" mkdir -p "$first_username_home/.config/lemana-vpn" "$first_username_home/.config/openconnect-lite" printf 'LEMANA_VPN_CREDENTIAL_SOURCE="keychain"\nLEMANA_VPN_USE_BITWARDEN="0"\nLEMANA_VPN_USE_TOUCHID="0"\n' > "$first_username_home/.config/lemana-vpn/env" printf 'username = ""\n' > "$first_username_home/.config/openconnect-lite/config.toml" FIRST_USERNAME_HOME="$first_username_home" \ ROOT="$ROOT" \ FAKE_PATH_BIN="$fake_path_bin" \ FAKE_OC_BIN="$fake_oc_bin" \ FAKE_WEBENGINE="$fake_webengine" \ FAKE_AUTHENTICATOR="$fake_authenticator" \ MANUAL_FULL_CAPTURE="$manual_full_capture" \ TMP_DIR="$TMP_DIR" \ expect <<'EXPECT' set timeout 30 log_user 0 set home $env(FIRST_USERNAME_HOME) spawn env \ HOME=$home \ PATH=$env(FAKE_PATH_BIN):$env(PATH) \ LEMANA_VPN_CONFIG_DIR=$home/.config/lemana-vpn \ OPENCONNECT_LITE_CONFIG_DIR=$home/.config/openconnect-lite \ LEMANA_VPN_OC_BIN=$env(FAKE_OC_BIN) \ LEMANA_VPN_OC_PYTHON=python3 \ LEMANA_VPN_WEBENGINE_PROCESS=$env(FAKE_WEBENGINE) \ LEMANA_VPN_AUTHENTICATOR=$env(FAKE_AUTHENTICATOR) \ LEMANA_VPN_PATCH_BACKUP_DIR=$env(TMP_DIR)/first-username-patch-backups \ LEMANA_VPN_DNS_CLEANUP=$env(TMP_DIR)/fake-dns-cleanup \ LEMANA_VPN_CONNECT_LOG=$env(TMP_DIR)/first-username.log \ LEMANA_VPN_CAPTURE_LAUNCH=$env(MANUAL_FULL_CAPTURE) \ bash $env(ROOT)/bin/vpn-lemanapro.sh --manual --json expect "Corporate LDAP username:" send "lemana-first-user\r" expect eof EXPECT grep -q 'LEMANA_VPN_USERNAME="lemana-first-user"' "$first_username_home/.config/lemana-vpn/env" grep -q 'username = "lemana-first-user"' "$first_username_home/.config/openconnect-lite/config.toml" fi manual_full_output="$( HOME="$HOME" \ PATH="$fake_path_bin:$PATH" \ LEMANA_VPN_USERNAME="lemana-manual-full-$$" \ LEMANA_VPN_CREDENTIAL_SOURCE=keychain \ LEMANA_VPN_OC_BIN="$fake_oc_bin" \ LEMANA_VPN_OC_PYTHON=python3 \ LEMANA_VPN_WEBENGINE_PROCESS="$fake_webengine" \ LEMANA_VPN_AUTHENTICATOR="$fake_authenticator" \ LEMANA_VPN_PATCH_BACKUP_DIR="$TMP_DIR/manual-full-patch-backups" \ LEMANA_VPN_DNS_CLEANUP="$TMP_DIR/fake-dns-cleanup" \ LEMANA_VPN_CONNECT_LOG="$TMP_DIR/manual-full.log" \ LEMANA_VPN_CAPTURE_LAUNCH="$manual_full_capture" \ bash "$ROOT/bin/vpn-lemanapro.sh" --manual-full --json )" printf '%s\n' "$manual_full_output" | grep -q '"event":"manual_sso","mode":"manual-keychain","diagnostic":true,"autofill":false,"totp":false,"submit":false' grep -q -- '--browser-display-mode shown' "$manual_full_capture" grep -q '^autofill_disable=1$' "$manual_full_capture" grep -q '^autofill_click=0$' "$manual_full_capture" grep -q '^autofill_totp=0$' "$manual_full_capture" fake_oc_python="$TMP_DIR/fake-oc-python" captured_totp="$TMP_DIR/captured-totp" cat > "$fake_oc_python" <<'SH' #!/bin/sh cat >/dev/null printf '%s' "${_VPN_TOTP_SECRET:-}" > "$LEMANA_VPN_CAPTURE_TOTP" SH chmod +x "$fake_oc_python" configure_output="$( printf 'ldap-password\notpauth://totp/Lemana:test?secret=abcd2345efgh6723&issuer=Lemana\n' | HOME="$HOME" \ LEMANA_VPN_USERNAME="lemana-configure-$$" \ LEMANA_VPN_CREDENTIAL_SOURCE=keychain \ LEMANA_VPN_OC_PYTHON="$fake_oc_python" \ LEMANA_VPN_CAPTURE_TOTP="$captured_totp" \ bash "$ROOT/bin/vpn-lemanapro.sh" --configure-keychain )" printf '%s\n' "$configure_output" | grep -q 'Credentials are ready in macOS Keychain' grep -q '^ABCD2345EFGH6723$' "$captured_totp" captured_login_totp="$TMP_DIR/captured-login-totp" configure_login_output="$( printf 'ldap-password\n' | HOME="$HOME" \ LEMANA_VPN_USERNAME="lemana-login-$$" \ LEMANA_VPN_AUTH_PROFILE=saved-login \ LEMANA_VPN_CREDENTIAL_SOURCE=keychain \ LEMANA_VPN_OC_PYTHON="$fake_oc_python" \ LEMANA_VPN_CAPTURE_TOTP="$captured_login_totp" \ bash "$ROOT/bin/vpn-lemanapro.sh" --configure-login )" printf '%s\n' "$configure_login_output" | grep -q 'Login and password are ready in macOS Keychain' if [ -s "$captured_login_totp" ]; then echo "configure-login must not store a TOTP seed" >&2 exit 1 fi fake_pwd="$TMP_DIR/fake-pwd" mkdir -p "$fake_pwd/bin" printf 'stale local cli\n' > "$fake_pwd/bin/vpn-lemanapro.sh" piped_output="$( cd "$fake_pwd" && LEMANA_VPN_RAW_BASE_URL="file://$ROOT" sh -s -- --dry-run --non-interactive --minimal --without-app < "$ROOT/install.sh" )" printf '%s\n' "$piped_output" | grep -q "curl -fsSL file://$ROOT/bin/vpn-lemanapro.sh" if printf '%s\n' "$piped_output" | grep -q "$fake_pwd/bin/vpn-lemanapro.sh"; then echo "piped install used stale PWD/bin/vpn-lemanapro.sh" >&2 exit 1 fi printf 'smoke ok\n'