evo-cli 0.34.0__tar.gz → 0.34.2__tar.gz
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.
- {evo_cli-0.34.0 → evo_cli-0.34.2}/PKG-INFO +1 -1
- evo_cli-0.34.2/evo_cli/VERSION +1 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/openvpn.py +40 -2
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli.egg-info/PKG-INFO +1 -1
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_openvpn.py +48 -4
- evo_cli-0.34.0/evo_cli/VERSION +0 -1
- {evo_cli-0.34.0 → evo_cli-0.34.2}/Containerfile +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/HISTORY.md +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/LICENSE +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/MANIFEST.in +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/README.md +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/__init__.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/__main__.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/base.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/cli.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/__init__.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/agent_toy.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/claude_code.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/cloudflare.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/cred.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/download.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/fix_claude.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/gdrive.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/gh.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/__init__.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_dag.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_git.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_hub.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_mirror.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_model.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_mutate.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_paths.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_render.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_server.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/check.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/clone.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/edit.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/pull.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/serve.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/show.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/views.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/index-Cs7hXRPJ.css +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/index-CuNOlaH7.js +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/index.html +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/hwid.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/hwid_reset.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/image.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/localproxy.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/mcp.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/miniconda.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/netcheck.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/ngrok.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/opencode.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/openvpn.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/plantuml.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/__init__.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/_dns.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/_dpi.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/_hosts.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/_page.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/_probe.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/_spoofdpi.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/check.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/fix.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/probe.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/status.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/serp.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/site2s.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/ssh.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/storage.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/sysmon.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/tts.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/update.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/wifi.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/console.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/credentials/__init__.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/credentials/doctor.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/credentials/google_oauth.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/credentials/migrate.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/credentials/oauth_flow.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/credentials/registry.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/credentials/store.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/credentials/sync.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/imaging/__init__.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/imaging/align.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/imaging/core.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/imaging/creds.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/imaging/errors.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/imaging/gemini.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/imaging/ncnn.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/mcp_registry.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/serp/__init__.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/serp/client.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/serp/creds.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/serp/errors.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/serp/install.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/serp/render.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/__init__.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/chunking.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/core.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/creds.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/errors.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/gemini.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/http.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/openai.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/player.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/vbee.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli.egg-info/SOURCES.txt +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli.egg-info/dependency_links.txt +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli.egg-info/entry_points.txt +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli.egg-info/requires.txt +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli.egg-info/top_level.txt +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/pyproject.toml +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/setup.cfg +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/__init__.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/fake_evo_agents.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/fixtures/hub/contract-0.2.0.json +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/fixtures/hub/mirror-demo.yaml +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_agent_toy.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_claude_code.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_cli.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_console.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_cred.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_download.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_fix_claude.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_gdrive.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_gh.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_harness.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_harness_clone.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_harness_dag.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_harness_hub.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_harness_show.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_image.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_mcp.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_ngrok.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_opencode.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_plantuml.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_route.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_serp.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_storage.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_sysmon.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_tts.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_update.py +0 -0
- {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_wifi.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.34.2
|
|
@@ -23,6 +23,8 @@ from urllib.parse import parse_qs, urlparse
|
|
|
23
23
|
from evo_cli.credentials.store import CredentialError, compile_flat, read_flat, set_value
|
|
24
24
|
|
|
25
25
|
APP = Path("/Applications/OpenVPN Connect/OpenVPN Connect.app/Contents/MacOS/OpenVPN Connect")
|
|
26
|
+
# OpenVPN 2.6's default --data-ciphers.
|
|
27
|
+
DATA_CIPHERS = ("AES-256-GCM", "AES-128-GCM", "CHACHA20-POLY1305")
|
|
26
28
|
INLINE = {"ca", "cert", "key", "tls-auth", "tls-crypt", "tls-crypt-v2", "extra-certs"}
|
|
27
29
|
# Only self-contained client profiles: never execute scripts/plugins or follow includes as root.
|
|
28
30
|
OPTIONS = {
|
|
@@ -184,6 +186,24 @@ def uses_credentials(text):
|
|
|
184
186
|
return any(words[0].removeprefix("--") == "auth-user-pass" for _, words in _directives(text))
|
|
185
187
|
|
|
186
188
|
|
|
189
|
+
def data_ciphers(text):
|
|
190
|
+
"""The --data-ciphers to pass for a profile that names only --cipher, else None.
|
|
191
|
+
|
|
192
|
+
OpenVPN 2.5 and Connect offer that cipher too; 2.6 no longer does, so a server that accepts nothing else
|
|
193
|
+
(Access Server profiles carry "cipher AES-256-CBC") ends with "no shared cipher". The defaults stay first.
|
|
194
|
+
"""
|
|
195
|
+
cipher = None
|
|
196
|
+
for _, words in _directives(text):
|
|
197
|
+
option = words[0].removeprefix("--")
|
|
198
|
+
if option == "data-ciphers":
|
|
199
|
+
return None
|
|
200
|
+
if option == "cipher" and len(words) == 2:
|
|
201
|
+
cipher = words[1]
|
|
202
|
+
if not cipher or cipher.upper() in DATA_CIPHERS:
|
|
203
|
+
return None
|
|
204
|
+
return ":".join([*DATA_CIPHERS, cipher])
|
|
205
|
+
|
|
206
|
+
|
|
187
207
|
def takes_default_route(text):
|
|
188
208
|
"""Whether a profile may take the default route. Only split-tunnel profiles can run beside another VPN.
|
|
189
209
|
|
|
@@ -503,12 +523,26 @@ def quote(value):
|
|
|
503
523
|
return '"' + value.replace("\\", "\\\\").replace('"', '\\"') + '"'
|
|
504
524
|
|
|
505
525
|
|
|
526
|
+
def rejection_reason(line):
|
|
527
|
+
"""The server's text in a Verification Failed notice, for the user; a dynamic challenge keeps only its prompt.
|
|
528
|
+
|
|
529
|
+
OpenVPN writes >PASSWORD:Verification Failed: 'Auth' ['REASON'] (the brackets are its own). A CRV1 challenge
|
|
530
|
+
carries a state ID and the username besides its prompt, so neither is shown.
|
|
531
|
+
"""
|
|
532
|
+
reason = line.partition("['")[2].rpartition("']")[0]
|
|
533
|
+
if reason.startswith("CRV1:"):
|
|
534
|
+
return "dynamic challenge, not supported: " + reason.split(":", 4)[-1]
|
|
535
|
+
return reason
|
|
536
|
+
|
|
537
|
+
|
|
506
538
|
def auth_commands(line, profile):
|
|
507
539
|
# OpenVPN itself caches server-issued session tokens. This notification needs no reply.
|
|
508
540
|
if line.startswith(">PASSWORD:Auth-Token:"):
|
|
509
541
|
return ""
|
|
510
542
|
if "Verification Failed" in line:
|
|
511
|
-
|
|
543
|
+
reason = rejection_reason(line)
|
|
544
|
+
detail = f": {reason}" if reason else "; check password/OTP enrollment before retrying"
|
|
545
|
+
raise CredentialError(f"VPN authentication rejected{detail}.")
|
|
512
546
|
if "Need 'Auth' username/password" not in line:
|
|
513
547
|
raise CredentialError("Unsupported authentication challenge (private-key password or dynamic challenge).")
|
|
514
548
|
username, password = profile.get("username", ""), profile.get("password", "")
|
|
@@ -621,6 +655,8 @@ def worker(name, timeout):
|
|
|
621
655
|
# With no password to ask for, a server can add a sign-in step only through the browser (Access
|
|
622
656
|
# Server SAML); announce it, or such a server refuses the client.
|
|
623
657
|
command += ["--setenv", "IV_SSO", "openurl,webauth"]
|
|
658
|
+
if ciphers := data_ciphers(profile["config"]):
|
|
659
|
+
command += ["--data-ciphers", ciphers]
|
|
624
660
|
pinned = [] if takes_default_route(profile["config"]) else remotes
|
|
625
661
|
if profile.get("otp_url"):
|
|
626
662
|
pinned = [*pinned, urlparse(profile["otp_url"]).hostname]
|
|
@@ -715,7 +751,9 @@ def worker(name, timeout):
|
|
|
715
751
|
state.pop("since", None)
|
|
716
752
|
if fields[1] == "AUTH_PENDING":
|
|
717
753
|
# Signing in happens in the browser: allow the time the server gives it.
|
|
718
|
-
|
|
754
|
+
# "timeout 180" in OpenVPN 2.6, a bare number in older notes.
|
|
755
|
+
wait = fields[2].split()[-1] if fields[2] else ""
|
|
756
|
+
wait = int(wait) if wait.isdigit() else 300
|
|
719
757
|
deadline = max(deadline, time.monotonic() + wait)
|
|
720
758
|
state["auth_expires"] = time.time() + wait
|
|
721
759
|
if fields[1] == "RECONNECTING":
|
|
@@ -261,10 +261,23 @@ def test_static_challenge_uses_scrv1_and_fresh_totp(monkeypatch):
|
|
|
261
261
|
assert "SCRV1:cGFzcw==:NjU0MzIx" in vpn.auth_commands(line, profile)
|
|
262
262
|
|
|
263
263
|
|
|
264
|
-
|
|
264
|
+
@pytest.mark.parametrize(
|
|
265
|
+
"line,shown,hidden",
|
|
266
|
+
[
|
|
267
|
+
(">PASSWORD:Verification Failed: 'Auth'", "check password/OTP enrollment", None),
|
|
268
|
+
(">PASSWORD:Verification Failed: 'Auth' ['SAML user does not match']", ": SAML user does not match.", None),
|
|
269
|
+
(
|
|
270
|
+
">PASSWORD:Verification Failed: 'Auth' ['CRV1:R,E:state-id-secret:dXNlcg==:Enter token PIN']",
|
|
271
|
+
"dynamic challenge, not supported: Enter token PIN",
|
|
272
|
+
"state-id-secret",
|
|
273
|
+
),
|
|
274
|
+
],
|
|
275
|
+
)
|
|
276
|
+
def test_auth_rejection_shows_the_reason_but_not_the_challenge_state(line, shown, hidden):
|
|
265
277
|
with pytest.raises(CredentialError) as exc:
|
|
266
|
-
vpn.auth_commands(
|
|
267
|
-
assert
|
|
278
|
+
vpn.auth_commands(line, {})
|
|
279
|
+
assert shown in str(exc.value)
|
|
280
|
+
assert not hidden or (hidden not in str(exc.value) and "dXNlcg==" not in str(exc.value))
|
|
268
281
|
|
|
269
282
|
|
|
270
283
|
def test_auth_token_is_a_notification_not_a_prompt():
|
|
@@ -354,6 +367,37 @@ def test_worker_pins_the_otp_host_even_for_a_full_tunnel(store, monkeypatch):
|
|
|
354
367
|
assert pinned == [["otp.example"]]
|
|
355
368
|
|
|
356
369
|
|
|
370
|
+
@pytest.mark.parametrize(
|
|
371
|
+
"extra,expected",
|
|
372
|
+
[
|
|
373
|
+
("", None),
|
|
374
|
+
("cipher AES-256-CBC\n", "AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305:AES-256-CBC"),
|
|
375
|
+
("cipher AES-256-GCM\n", None),
|
|
376
|
+
("cipher AES-256-CBC\ndata-ciphers AES-256-GCM\n", None),
|
|
377
|
+
],
|
|
378
|
+
)
|
|
379
|
+
def test_a_profile_cipher_is_offered_after_the_defaults(extra, expected):
|
|
380
|
+
assert vpn.data_ciphers(CONFIG + extra) == expected
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def test_worker_passes_the_profile_cipher(store, monkeypatch):
|
|
384
|
+
vpn.save_profile("m1", {"config": CERT_ONLY + "cipher AES-256-CBC\n"})
|
|
385
|
+
monkeypatch.setattr(sys, "stdin", io.StringIO("\n"))
|
|
386
|
+
monkeypatch.setattr(vpn.os, "geteuid", lambda: 0)
|
|
387
|
+
monkeypatch.setattr(vpn.shutil, "which", lambda _: "/fake/openvpn")
|
|
388
|
+
monkeypatch.setattr(vpn, "pin_remotes", lambda command, hosts: command)
|
|
389
|
+
started = []
|
|
390
|
+
|
|
391
|
+
def popen(command, **_):
|
|
392
|
+
started.append(command)
|
|
393
|
+
raise OSError("stop before running anything")
|
|
394
|
+
|
|
395
|
+
monkeypatch.setattr(vpn.subprocess, "Popen", popen)
|
|
396
|
+
vpn.worker("m1", 10)
|
|
397
|
+
command = started[0]
|
|
398
|
+
assert command[command.index("--data-ciphers") + 1].endswith(":AES-256-CBC")
|
|
399
|
+
|
|
400
|
+
|
|
357
401
|
@pytest.mark.parametrize(
|
|
358
402
|
"message,expected",
|
|
359
403
|
[
|
|
@@ -489,7 +533,7 @@ with socket.socket(socket.AF_UNIX) as s:
|
|
|
489
533
|
held = b'hold off\\n' not in seen
|
|
490
534
|
if sys.argv[2] == 'webauth':
|
|
491
535
|
# Access Server SAML: no password prompt, a browser sign-in instead.
|
|
492
|
-
s.sendall(b'>STATE:1,AUTH_PENDING,120,,,,,\\n')
|
|
536
|
+
s.sendall(b'>STATE:1,AUTH_PENDING,timeout 120,,,,,\\n')
|
|
493
537
|
s.sendall(b'>INFOMSG:WEB_AUTH::https://vpn.example/auth/1\\n')
|
|
494
538
|
else:
|
|
495
539
|
auth()
|
evo_cli-0.34.0/evo_cli/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.34.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|