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.
Files changed (156) hide show
  1. {evo_cli-0.34.0 → evo_cli-0.34.2}/PKG-INFO +1 -1
  2. evo_cli-0.34.2/evo_cli/VERSION +1 -0
  3. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/openvpn.py +40 -2
  4. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli.egg-info/PKG-INFO +1 -1
  5. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_openvpn.py +48 -4
  6. evo_cli-0.34.0/evo_cli/VERSION +0 -1
  7. {evo_cli-0.34.0 → evo_cli-0.34.2}/Containerfile +0 -0
  8. {evo_cli-0.34.0 → evo_cli-0.34.2}/HISTORY.md +0 -0
  9. {evo_cli-0.34.0 → evo_cli-0.34.2}/LICENSE +0 -0
  10. {evo_cli-0.34.0 → evo_cli-0.34.2}/MANIFEST.in +0 -0
  11. {evo_cli-0.34.0 → evo_cli-0.34.2}/README.md +0 -0
  12. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/__init__.py +0 -0
  13. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/__main__.py +0 -0
  14. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/base.py +0 -0
  15. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/cli.py +0 -0
  16. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/__init__.py +0 -0
  17. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/agent_toy.py +0 -0
  18. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/claude_code.py +0 -0
  19. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/cloudflare.py +0 -0
  20. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/cred.py +0 -0
  21. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/download.py +0 -0
  22. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/fix_claude.py +0 -0
  23. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/gdrive.py +0 -0
  24. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/gh.py +0 -0
  25. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/__init__.py +0 -0
  26. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_dag.py +0 -0
  27. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_git.py +0 -0
  28. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_hub.py +0 -0
  29. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_mirror.py +0 -0
  30. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_model.py +0 -0
  31. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_mutate.py +0 -0
  32. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_paths.py +0 -0
  33. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_render.py +0 -0
  34. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/_server.py +0 -0
  35. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/check.py +0 -0
  36. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/clone.py +0 -0
  37. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/edit.py +0 -0
  38. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/pull.py +0 -0
  39. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/serve.py +0 -0
  40. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/show.py +0 -0
  41. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/views.py +0 -0
  42. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/index-Cs7hXRPJ.css +0 -0
  43. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/index-CuNOlaH7.js +0 -0
  44. {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
  45. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  46. {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
  47. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  48. {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
  49. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  50. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  51. {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
  52. {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
  53. {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
  54. {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
  55. {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
  56. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/harness/web/index.html +0 -0
  57. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/hwid.py +0 -0
  58. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/hwid_reset.py +0 -0
  59. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/image.py +0 -0
  60. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/localproxy.py +0 -0
  61. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/mcp.py +0 -0
  62. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/miniconda.py +0 -0
  63. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/netcheck.py +0 -0
  64. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/ngrok.py +0 -0
  65. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/opencode.py +0 -0
  66. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/openvpn.py +0 -0
  67. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/plantuml.py +0 -0
  68. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/__init__.py +0 -0
  69. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/_dns.py +0 -0
  70. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/_dpi.py +0 -0
  71. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/_hosts.py +0 -0
  72. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/_page.py +0 -0
  73. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/_probe.py +0 -0
  74. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/_spoofdpi.py +0 -0
  75. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/check.py +0 -0
  76. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/fix.py +0 -0
  77. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/probe.py +0 -0
  78. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/route/status.py +0 -0
  79. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/serp.py +0 -0
  80. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/site2s.py +0 -0
  81. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/ssh.py +0 -0
  82. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/storage.py +0 -0
  83. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/sysmon.py +0 -0
  84. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/tts.py +0 -0
  85. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/update.py +0 -0
  86. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/commands/wifi.py +0 -0
  87. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/console.py +0 -0
  88. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/credentials/__init__.py +0 -0
  89. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/credentials/doctor.py +0 -0
  90. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/credentials/google_oauth.py +0 -0
  91. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/credentials/migrate.py +0 -0
  92. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/credentials/oauth_flow.py +0 -0
  93. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/credentials/registry.py +0 -0
  94. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/credentials/store.py +0 -0
  95. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/credentials/sync.py +0 -0
  96. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/imaging/__init__.py +0 -0
  97. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/imaging/align.py +0 -0
  98. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/imaging/core.py +0 -0
  99. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/imaging/creds.py +0 -0
  100. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/imaging/errors.py +0 -0
  101. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/imaging/gemini.py +0 -0
  102. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/imaging/ncnn.py +0 -0
  103. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/mcp_registry.py +0 -0
  104. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/serp/__init__.py +0 -0
  105. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/serp/client.py +0 -0
  106. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/serp/creds.py +0 -0
  107. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/serp/errors.py +0 -0
  108. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/serp/install.py +0 -0
  109. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/serp/render.py +0 -0
  110. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/__init__.py +0 -0
  111. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/chunking.py +0 -0
  112. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/core.py +0 -0
  113. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/creds.py +0 -0
  114. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/errors.py +0 -0
  115. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/gemini.py +0 -0
  116. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/http.py +0 -0
  117. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/openai.py +0 -0
  118. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/player.py +0 -0
  119. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli/tts/vbee.py +0 -0
  120. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli.egg-info/SOURCES.txt +0 -0
  121. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli.egg-info/dependency_links.txt +0 -0
  122. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli.egg-info/entry_points.txt +0 -0
  123. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli.egg-info/requires.txt +0 -0
  124. {evo_cli-0.34.0 → evo_cli-0.34.2}/evo_cli.egg-info/top_level.txt +0 -0
  125. {evo_cli-0.34.0 → evo_cli-0.34.2}/pyproject.toml +0 -0
  126. {evo_cli-0.34.0 → evo_cli-0.34.2}/setup.cfg +0 -0
  127. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/__init__.py +0 -0
  128. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/fake_evo_agents.py +0 -0
  129. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/fixtures/hub/contract-0.2.0.json +0 -0
  130. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/fixtures/hub/mirror-demo.yaml +0 -0
  131. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_agent_toy.py +0 -0
  132. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_claude_code.py +0 -0
  133. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_cli.py +0 -0
  134. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_console.py +0 -0
  135. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_cred.py +0 -0
  136. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_download.py +0 -0
  137. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_fix_claude.py +0 -0
  138. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_gdrive.py +0 -0
  139. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_gh.py +0 -0
  140. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_harness.py +0 -0
  141. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_harness_clone.py +0 -0
  142. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_harness_dag.py +0 -0
  143. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_harness_hub.py +0 -0
  144. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_harness_show.py +0 -0
  145. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_image.py +0 -0
  146. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_mcp.py +0 -0
  147. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_ngrok.py +0 -0
  148. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_opencode.py +0 -0
  149. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_plantuml.py +0 -0
  150. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_route.py +0 -0
  151. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_serp.py +0 -0
  152. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_storage.py +0 -0
  153. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_sysmon.py +0 -0
  154. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_tts.py +0 -0
  155. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_update.py +0 -0
  156. {evo_cli-0.34.0 → evo_cli-0.34.2}/tests/test_wifi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: evo_cli
3
- Version: 0.34.0
3
+ Version: 0.34.2
4
4
  Summary: Evolution CLI - a developer toolbox for setting up dev machines
5
5
  Author: maycuatroi
6
6
  Project-URL: Homepage, https://github.com/maycuatroi/evo-cli
@@ -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
- raise CredentialError("VPN authentication rejected; check password/OTP enrollment before retrying.")
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
- wait = int(fields[2]) if fields[2].isdigit() else 300
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":
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: evo_cli
3
- Version: 0.34.0
3
+ Version: 0.34.2
4
4
  Summary: Evolution CLI - a developer toolbox for setting up dev machines
5
5
  Author: maycuatroi
6
6
  Project-URL: Homepage, https://github.com/maycuatroi/evo-cli
@@ -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
- def test_auth_rejection_does_not_retry_or_leak_challenge():
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(">PASSWORD:Verification Failed: 'Auth' ['sensitive']", {})
267
- assert "sensitive" not in str(exc.value)
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()
@@ -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