Добавь ручной режим SSO
This commit is contained in:
17
README.md
17
README.md
@@ -307,6 +307,7 @@ vpn # подключиться
|
||||
vpn --status # статус без нового подключения
|
||||
vpn --status --json # статус в JSON
|
||||
vpn-debug # видимый браузер и debug-логи
|
||||
vpn --manual-sso --debug # видимый браузер без auto-fill/auto-submit Keycloak
|
||||
vpn-fix-dns # сбросить корпоративные DNS после аварийного завершения
|
||||
open ~/Applications/LemanaVPN.app # открыть Swift-приложение в menu bar
|
||||
```
|
||||
@@ -369,8 +370,10 @@ curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh \
|
||||
| --- | --- | --- |
|
||||
| `minimal -> offscreen` | Меняет Qt platform mode для скрытого браузера | `minimal` падает с Qt WebEngine на macOS |
|
||||
| `input/change events` | После `value = ...` отправляет DOM events | Keycloak не реагирует на прямую запись value |
|
||||
| `native value setter` | Заполняет поля через нативный setter `HTMLInputElement` | React/Keycloak корректнее видит изменение значения |
|
||||
| `URL guard` | Проверяет `location.href` через `new RegExp(...)` перед auto-fill | Qt игнорирует `@include`, без guard auto-fill может кликнуть Cisco ACS и сломать SAML |
|
||||
| `submit click guard` | Кликает submit один раз на страницу и только после заполнения поля | Без guard hidden-браузер может зациклиться на Keycloak `login-actions/authenticate` |
|
||||
| `manual SSO disable` | Позволяет отключить auto-fill через `LEMANA_VPN_AUTOFILL_DISABLE=1` | Нужен для ручной диагностики в видимом браузере |
|
||||
|
||||
Перед первым изменением CLI сохраняет оригинальный файл:
|
||||
|
||||
@@ -404,7 +407,7 @@ vpn --status
|
||||
tail -f ~/Library/Logs/LemanaVPN-openconnect-lite.log
|
||||
```
|
||||
|
||||
В обычном режиме CLI также печатает heartbeat `Still waiting for SSO/openconnect-lite...`, чтобы было понятно, что процесс живой. В `vpn-debug` дополнительно показываются raw-логи и видимый браузер.
|
||||
В обычном режиме CLI также печатает heartbeat `Still waiting for SSO/openconnect-lite...` до успешного подключения, чтобы было понятно, что процесс живой. В `vpn-debug` дополнительно показываются raw-логи и видимый браузер.
|
||||
|
||||
Если в логе повторяется один и тот же URL вида `employee.auth.lemanapro.ru/realms/employee/login-actions/authenticate`, значит hidden-браузер застрял на Keycloak до перехода в Cisco ACS. Сначала обнови и примени runtime-патчи без подключения:
|
||||
|
||||
@@ -419,14 +422,24 @@ vpn-lemanapro.sh --patch-only
|
||||
vpn-debug
|
||||
```
|
||||
|
||||
Если нужно самому посмотреть форму Keycloak и исключить влияние автоматического заполнения:
|
||||
|
||||
```sh
|
||||
vpn --manual-sso --debug
|
||||
```
|
||||
|
||||
В этом режиме браузер видимый, а `openconnect-lite` не запускает auto-fill/auto-submit. LDAP-пароль и TOTP seed всё ещё берутся из Keychain, но ввод на странице выполняется вручную.
|
||||
|
||||
Если установка падает на строке `install: /usr/local/sbin/...: No such file or directory`, значит на машине не было `/usr/local/sbin`. Актуальный `install.sh` создаёт эту директорию сам; достаточно повторить установку свежей командой `curl`.
|
||||
|
||||
CLI перед подключением патчит `openconnect-lite`:
|
||||
|
||||
- `minimal` -> `offscreen`, чтобы Qt WebEngine не падал на macOS;
|
||||
- добавляет `input` и `change` events для Keycloak auto-fill;
|
||||
- добавляет URL guard, чтобы auto-fill не кликал submit на Cisco ACS.
|
||||
- заполняет поля через native value setter;
|
||||
- добавляет URL guard, чтобы auto-fill не кликал submit на Cisco ACS;
|
||||
- добавляет submit click guard, чтобы auto-fill не отправлял одну и ту же Keycloak форму бесконечно.
|
||||
- добавляет manual SSO disable для видимой ручной диагностики без auto-fill.
|
||||
|
||||
## Удаление
|
||||
|
||||
|
||||
@@ -4,11 +4,23 @@ set -euo pipefail
|
||||
CONFIG_DIR="${LEMANA_VPN_CONFIG_DIR:-$HOME/.config/lemana-vpn}"
|
||||
CONFIG_FILE="$CONFIG_DIR/env"
|
||||
|
||||
_ENV_LEMANA_VPN_USERNAME="${LEMANA_VPN_USERNAME+x}${LEMANA_VPN_USERNAME-}"
|
||||
_ENV_LEMANA_VPN_BW_ITEM="${LEMANA_VPN_BW_ITEM+x}${LEMANA_VPN_BW_ITEM-}"
|
||||
_ENV_LEMANA_VPN_USE_BITWARDEN="${LEMANA_VPN_USE_BITWARDEN+x}${LEMANA_VPN_USE_BITWARDEN-}"
|
||||
_ENV_LEMANA_VPN_USE_TOUCHID="${LEMANA_VPN_USE_TOUCHID+x}${LEMANA_VPN_USE_TOUCHID-}"
|
||||
_ENV_LEMANA_VPN_DNS_CLEANUP="${LEMANA_VPN_DNS_CLEANUP+x}${LEMANA_VPN_DNS_CLEANUP-}"
|
||||
|
||||
if [[ -f "$CONFIG_FILE" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$CONFIG_FILE"
|
||||
fi
|
||||
|
||||
[[ "${_ENV_LEMANA_VPN_USERNAME:0:1}" == "x" ]] && LEMANA_VPN_USERNAME="${_ENV_LEMANA_VPN_USERNAME:1}"
|
||||
[[ "${_ENV_LEMANA_VPN_BW_ITEM:0:1}" == "x" ]] && LEMANA_VPN_BW_ITEM="${_ENV_LEMANA_VPN_BW_ITEM:1}"
|
||||
[[ "${_ENV_LEMANA_VPN_USE_BITWARDEN:0:1}" == "x" ]] && LEMANA_VPN_USE_BITWARDEN="${_ENV_LEMANA_VPN_USE_BITWARDEN:1}"
|
||||
[[ "${_ENV_LEMANA_VPN_USE_TOUCHID:0:1}" == "x" ]] && LEMANA_VPN_USE_TOUCHID="${_ENV_LEMANA_VPN_USE_TOUCHID:1}"
|
||||
[[ "${_ENV_LEMANA_VPN_DNS_CLEANUP:0:1}" == "x" ]] && LEMANA_VPN_DNS_CLEANUP="${_ENV_LEMANA_VPN_DNS_CLEANUP:1}"
|
||||
|
||||
OC_VENV="${LEMANA_VPN_OC_VENV:-$HOME/.local/pipx/venvs/openconnect-lite}"
|
||||
OC_PYTHON="${LEMANA_VPN_OC_PYTHON:-$OC_VENV/bin/python}"
|
||||
OC_BIN="${LEMANA_VPN_OC_BIN:-$HOME/.local/bin/openconnect-lite}"
|
||||
@@ -36,6 +48,7 @@ JSON_MODE=false
|
||||
STATUS_MODE=false
|
||||
CONFIGURE_KEYCHAIN_MODE=false
|
||||
PATCH_ONLY_MODE=false
|
||||
MANUAL_SSO_MODE=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
@@ -44,9 +57,10 @@ for arg in "$@"; do
|
||||
--status) STATUS_MODE=true ;;
|
||||
--configure-keychain) CONFIGURE_KEYCHAIN_MODE=true ;;
|
||||
--patch-only) PATCH_ONLY_MODE=true ;;
|
||||
--manual-sso) MANUAL_SSO_MODE=true ;;
|
||||
--help|-h)
|
||||
cat <<'HELP'
|
||||
Usage: vpn-lemanapro.sh [--debug] [--json] [--status] [--configure-keychain] [--patch-only]
|
||||
Usage: vpn-lemanapro.sh [--debug] [--json] [--status] [--configure-keychain] [--patch-only] [--manual-sso]
|
||||
|
||||
--status Show current VPN status without connecting
|
||||
--status --json Show current VPN status as JSON
|
||||
@@ -54,6 +68,7 @@ Usage: vpn-lemanapro.sh [--debug] [--json] [--status] [--configure-keychain] [--
|
||||
--json Emit JSON Lines events for UI wrappers
|
||||
--configure-keychain Prompt for LDAP password and TOTP secret, then save them to Keychain
|
||||
--patch-only Apply openconnect-lite runtime patches and exit
|
||||
--manual-sso Show browser and disable Keycloak auto-fill/auto-submit
|
||||
HELP
|
||||
exit 0
|
||||
;;
|
||||
@@ -325,9 +340,47 @@ if src != original:
|
||||
messages.append("minimal -> offscreen")
|
||||
original = src
|
||||
|
||||
if "import os" not in src:
|
||||
src = src.replace("import json\n", "import json\nimport os\n")
|
||||
messages.append("autofill debug import")
|
||||
|
||||
if "Autologin disabled by Lemana VPN" not in src:
|
||||
old_autologin = ''' if credentials:
|
||||
logger.info("Initiating autologin", cred=credentials)
|
||||
'''
|
||||
new_autologin = ''' if os.environ.get("LEMANA_VPN_AUTOFILL_DISABLE") == "1":
|
||||
logger.info("Autologin disabled by Lemana VPN")
|
||||
elif credentials:
|
||||
logger.info("Initiating autologin", cred=credentials)
|
||||
'''
|
||||
if old_autologin not in src:
|
||||
print("Cannot apply manual SSO patch: unsupported openconnect-lite source", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
src = src.replace(old_autologin, new_autologin)
|
||||
messages.append("manual SSO disable")
|
||||
|
||||
if 'console.info("lemana autofill: " + message);' in src:
|
||||
src = src.replace(
|
||||
'console.info("lemana autofill: " + message);',
|
||||
'console.warn("lemana autofill: " + message);',
|
||||
)
|
||||
messages.append("autofill diagnostics warning log")
|
||||
|
||||
plain_value_set = 'elem.value = {value}; window.__lemanaVpnFilled = true;'
|
||||
native_value_set = 'var valueSetter = Object.getOwnPropertyDescriptor(elem.__proto__, "value") || Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value"); if (valueSetter && valueSetter.set) {{ valueSetter.set.call(elem, {value}); }} else {{ elem.value = {value}; }} window.__lemanaVpnFilled = true;'
|
||||
if plain_value_set in src:
|
||||
src = src.replace(plain_value_set, native_value_set)
|
||||
messages.append("native value setter")
|
||||
|
||||
plain_click = 'elem.dispatchEvent(new Event("focus")); elem.click();'
|
||||
delayed_click = 'elem.dispatchEvent(new Event("focus")); setTimeout(function() {{ if (document.contains(elem)) {{ elem.click(); }} }}, 250);'
|
||||
if plain_click in src:
|
||||
src = src.replace(plain_click, delayed_click)
|
||||
messages.append("delayed submit click")
|
||||
|
||||
old_fill = 'elem.dispatchEvent(new Event("focus")); elem.value = {value}; elem.dispatchEvent(new Event("blur"));'
|
||||
fill_with_events = 'elem.dispatchEvent(new Event("focus")); elem.value = {value}; elem.dispatchEvent(new Event("input", {{bubbles: true}})); elem.dispatchEvent(new Event("change", {{bubbles: true}})); elem.dispatchEvent(new Event("blur"));'
|
||||
new_fill = 'elem.dispatchEvent(new Event("focus")); elem.value = {value}; window.__lemanaVpnFilled = true; elem.dispatchEvent(new Event("input", {{bubbles: true}})); elem.dispatchEvent(new Event("change", {{bubbles: true}})); elem.dispatchEvent(new Event("blur"));'
|
||||
new_fill = 'elem.dispatchEvent(new Event("focus")); var valueSetter = Object.getOwnPropertyDescriptor(elem.__proto__, "value") || Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value"); if (valueSetter && valueSetter.set) {{ valueSetter.set.call(elem, {value}); }} else {{ elem.value = {value}; }} window.__lemanaVpnFilled = true; elem.dispatchEvent(new Event("input", {{bubbles: true}})); elem.dispatchEvent(new Event("change", {{bubbles: true}})); elem.dispatchEvent(new Event("blur"));'
|
||||
if old_fill in src:
|
||||
src = src.replace(old_fill, new_fill)
|
||||
messages.append("input/change events")
|
||||
@@ -336,11 +389,29 @@ elif fill_with_events in src:
|
||||
messages.append("fill marker")
|
||||
|
||||
old_click = 'var elem = document.querySelector({selector}); if (elem) {{ elem.dispatchEvent(new Event("focus")); elem.click(); }}'
|
||||
new_click = 'var elem = document.querySelector({selector}); if (elem && !elem.disabled && elem.offsetParent !== null && window.__lemanaVpnFilled) {{ window.__lemanaVpnClicked = window.__lemanaVpnClicked || {{}}; var clickKey = location.href + "|" + {selector}; if (!window.__lemanaVpnClicked[clickKey]) {{ window.__lemanaVpnClicked[clickKey] = true; elem.dispatchEvent(new Event("focus")); elem.click(); }} }}'
|
||||
new_click = 'var elem = document.querySelector({selector}); if (elem && !elem.disabled && elem.offsetParent !== null && window.__lemanaVpnFilled) {{ window.__lemanaVpnClicked = window.__lemanaVpnClicked || {{}}; var clickKey = location.href + "|" + {selector}; if (!window.__lemanaVpnClicked[clickKey]) {{ window.__lemanaVpnClicked[clickKey] = true; elem.dispatchEvent(new Event("focus")); setTimeout(function() {{ if (document.contains(elem)) {{ elem.click(); }} }}, 250); }} }}'
|
||||
if old_click in src:
|
||||
src = src.replace(old_click, new_click)
|
||||
messages.append("submit click guard")
|
||||
|
||||
stop_plain = 'var elem = document.querySelector({selector}); if (elem) {{ return; }}'
|
||||
stop_debug = 'var elem = document.querySelector({selector}); if (elem && elem.offsetParent !== null && (elem.textContent || "").trim()) {{ _afLog("stop selector=" + {selector} + " text=" + (elem.textContent || "").trim().slice(0, 80)); return; }}'
|
||||
if stop_plain in src:
|
||||
src = src.replace(stop_plain, stop_debug)
|
||||
messages.append("visible stop guard")
|
||||
|
||||
fill_plain = 'var elem = document.querySelector({selector}); if (elem) {{ elem.dispatchEvent(new Event("focus")); elem.value = {value}; window.__lemanaVpnFilled = true; elem.dispatchEvent(new Event("input", {{bubbles: true}})); elem.dispatchEvent(new Event("change", {{bubbles: true}})); elem.dispatchEvent(new Event("blur")); }}'
|
||||
fill_debug = 'var elem = document.querySelector({selector}); if (elem) {{ _afLog("fill " + {rule.fill!r} + " selector=" + {selector} + " tag=" + elem.tagName + " type=" + (elem.type || "") + " id=" + (elem.id || "") + " name=" + (elem.name || "") + " value_len=" + String({value}.length)); elem.dispatchEvent(new Event("focus")); var valueSetter = Object.getOwnPropertyDescriptor(elem.__proto__, "value") || Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value"); if (valueSetter && valueSetter.set) {{ valueSetter.set.call(elem, {value}); }} else {{ elem.value = {value}; }} window.__lemanaVpnFilled = true; elem.dispatchEvent(new Event("input", {{bubbles: true}})); elem.dispatchEvent(new Event("change", {{bubbles: true}})); elem.dispatchEvent(new Event("blur")); }} else {{ _afLog("missing " + {rule.fill!r} + " selector=" + {selector}); }}'
|
||||
if fill_plain in src and "_afLog(\"fill \"" not in src:
|
||||
src = src.replace(fill_plain, fill_debug)
|
||||
messages.append("fill diagnostics")
|
||||
|
||||
click_plain = 'var elem = document.querySelector({selector}); if (elem && !elem.disabled && elem.offsetParent !== null && window.__lemanaVpnFilled) {{ window.__lemanaVpnClicked = window.__lemanaVpnClicked || {{}}; var clickKey = location.href + "|" + {selector}; if (!window.__lemanaVpnClicked[clickKey]) {{ window.__lemanaVpnClicked[clickKey] = true; elem.dispatchEvent(new Event("focus")); elem.click(); }} }}'
|
||||
click_debug = 'var elem = document.querySelector({selector}); window.__lemanaVpnClicked = window.__lemanaVpnClicked || {{}}; var clickKey = location.href + "|" + {selector}; _afLog("click-check selector=" + {selector} + " found=" + Boolean(elem) + " filled=" + Boolean(window.__lemanaVpnFilled) + " disabled=" + Boolean(elem && elem.disabled) + " visible=" + Boolean(elem && elem.offsetParent !== null) + " already=" + Boolean(window.__lemanaVpnClicked[clickKey])); if (elem && !elem.disabled && elem.offsetParent !== null && window.__lemanaVpnFilled && !window.__lemanaVpnClicked[clickKey]) {{ window.__lemanaVpnClicked[clickKey] = true; _afLog("click selector=" + {selector}); elem.dispatchEvent(new Event("focus")); setTimeout(function() {{ if (document.contains(elem)) {{ elem.click(); }} }}, 250); }}'
|
||||
if click_plain in src and 'click-check selector=' not in src:
|
||||
src = src.replace(click_plain, click_debug)
|
||||
messages.append("click diagnostics")
|
||||
|
||||
if "new RegExp" not in src:
|
||||
old_block = ''' script.setSourceCode(
|
||||
f"""
|
||||
@@ -357,6 +428,7 @@ autoFill();
|
||||
)'''
|
||||
new_block = ''' regex_str = "^" + url_pattern.replace(".", "\\\\.").replace("*", ".*") + "$"
|
||||
js_regex_str = json.dumps(regex_str)
|
||||
js_debug = "true" if os.environ.get("LEMANA_VPN_AUTOFILL_DEBUG") == "1" else "false"
|
||||
script.setSourceCode(
|
||||
f"""
|
||||
// ==UserScript==
|
||||
@@ -365,6 +437,12 @@ autoFill();
|
||||
|
||||
var _afUrlPattern = new RegExp({js_regex_str});
|
||||
var _afRun = 0;
|
||||
var _afDebug = {js_debug};
|
||||
function _afLog(message) {{
|
||||
if (_afDebug && _afRun <= 5) {{
|
||||
console.warn("lemana autofill: " + message);
|
||||
}}
|
||||
}}
|
||||
function autoFill() {{
|
||||
if (!_afUrlPattern.test(location.href.split('?')[0]) && !_afUrlPattern.test(location.href)) {{
|
||||
_afRun++;
|
||||
@@ -384,11 +462,49 @@ autoFill();
|
||||
src = src.replace(old_block, new_block)
|
||||
messages.append("URL guard")
|
||||
|
||||
if "js_debug =" not in src:
|
||||
marker = ''' js_regex_str = json.dumps(regex_str)
|
||||
script.setSourceCode(
|
||||
'''
|
||||
if marker in src:
|
||||
src = src.replace(marker, ''' js_regex_str = json.dumps(regex_str)
|
||||
js_debug = "true" if os.environ.get("LEMANA_VPN_AUTOFILL_DEBUG") == "1" else "false"
|
||||
script.setSourceCode(
|
||||
''')
|
||||
messages.append("autofill debug flag")
|
||||
elif "_afLog(" in src:
|
||||
print("Cannot apply autofill debug flag patch: unsupported openconnect-lite source", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if "function _afLog" not in src:
|
||||
marker = '''var _afUrlPattern = new RegExp({js_regex_str});
|
||||
var _afRun = 0;
|
||||
function autoFill() {{
|
||||
'''
|
||||
if marker in src:
|
||||
src = src.replace(marker, '''var _afUrlPattern = new RegExp({js_regex_str});
|
||||
var _afRun = 0;
|
||||
var _afDebug = {js_debug};
|
||||
function _afLog(message) {{
|
||||
if (_afDebug && _afRun <= 5) {{
|
||||
console.warn("lemana autofill: " + message);
|
||||
}}
|
||||
}}
|
||||
function autoFill() {{
|
||||
''')
|
||||
messages.append("autofill diagnostics")
|
||||
elif "_afLog(" in src:
|
||||
print("Cannot apply autofill diagnostics patch: unsupported openconnect-lite source", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
page_state = ''' if (window.__lemanaVpnPageHref !== location.href) {{
|
||||
window.__lemanaVpnPageHref = location.href;
|
||||
window.__lemanaVpnFilled = false;
|
||||
window.__lemanaVpnClicked = {{}};
|
||||
}}
|
||||
if (_afDebug && _afRun === 1 && document.body) {{
|
||||
_afLog("body=" + (document.body.innerText || "").replace(/\\s+/g, " ").trim().slice(0, 180));
|
||||
}}
|
||||
'''
|
||||
if "window.__lemanaVpnPageHref" not in src:
|
||||
marker = ''' _afRun++;
|
||||
@@ -404,6 +520,20 @@ if "window.__lemanaVpnPageHref" not in src:
|
||||
''')
|
||||
messages.append("page state guard")
|
||||
|
||||
if 'body=" + (document.body.innerText || "")' not in src:
|
||||
marker = ''' if (window.__lemanaVpnPageHref !== location.href) {{
|
||||
window.__lemanaVpnPageHref = location.href;
|
||||
window.__lemanaVpnFilled = false;
|
||||
window.__lemanaVpnClicked = {{}};
|
||||
}}
|
||||
'''
|
||||
if marker in src:
|
||||
src = src.replace(marker, marker + ''' if (_afDebug && _afRun === 1 && document.body) {{
|
||||
_afLog("body=" + (document.body.innerText || "").replace(/\\s+/g, " ").trim().slice(0, 180));
|
||||
}}
|
||||
''')
|
||||
messages.append("body diagnostics")
|
||||
|
||||
if src != before:
|
||||
backup_dir.mkdir(parents=True, exist_ok=True)
|
||||
if not backup_file.exists():
|
||||
@@ -649,6 +779,7 @@ _filter_output() {
|
||||
fi
|
||||
|
||||
if [[ "$line" =~ Session\ authentication\ will\ expire\ at\ (.+) ]]; then
|
||||
_stop_connect_progress
|
||||
local expiry_str="${BASH_REMATCH[1]}"
|
||||
local expiry_ts expiry_local expiry_iso now_ts remaining hours mins
|
||||
expiry_ts="$(date -jf "%a %b %d %H:%M:%S %Y" "$expiry_str" "+%s" 2>/dev/null || true)"
|
||||
@@ -713,15 +844,25 @@ trap '_stop_connect_progress; _dns_cleanup; _clear_status' EXIT
|
||||
|
||||
display_mode="hidden"
|
||||
log_level=""
|
||||
autofill_debug="${LEMANA_VPN_AUTOFILL_DEBUG:-0}"
|
||||
autofill_disable="${LEMANA_VPN_AUTOFILL_DISABLE:-0}"
|
||||
if $MANUAL_SSO_MODE; then
|
||||
display_mode="shown"
|
||||
autofill_disable="1"
|
||||
_emit '{"event":"manual_sso","autofill":false}' "Manual SSO mode: browser is visible, Keycloak auto-fill is disabled."
|
||||
fi
|
||||
if $DEBUG; then
|
||||
display_mode="shown"
|
||||
log_level="--log-level debug"
|
||||
autofill_debug="1"
|
||||
fi
|
||||
|
||||
reconnect_count=0
|
||||
while true; do
|
||||
_start_connect_progress
|
||||
QTWEBENGINE_CHROMIUM_FLAGS="--disable-gpu" \
|
||||
LEMANA_VPN_AUTOFILL_DEBUG="$autofill_debug" \
|
||||
LEMANA_VPN_AUTOFILL_DISABLE="$autofill_disable" \
|
||||
"$OC_BIN" --browser-display-mode "$display_mode" $log_level 2>&1 \
|
||||
| _filter_output
|
||||
exit_code=${PIPESTATUS[0]}
|
||||
|
||||
@@ -35,6 +35,8 @@ printf '%s\n' "$status_json" | grep -q '"app":'
|
||||
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 -- '--manual-sso' "$ROOT/bin/vpn-lemanapro.sh"
|
||||
grep -q 'LEMANA_VPN_AUTOFILL_DISABLE' "$ROOT/bin/vpn-lemanapro.sh"
|
||||
grep -q '__lemanaVpnClicked' "$ROOT/bin/vpn-lemanapro.sh"
|
||||
|
||||
status_text="$(bash "$ROOT/bin/vpn-lemanapro.sh" --status)"
|
||||
|
||||
Reference in New Issue
Block a user