flowproof 0.23.28__tar.gz → 0.23.30__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 (190) hide show
  1. {flowproof-0.23.28 → flowproof-0.23.30}/Cargo.lock +45 -55
  2. {flowproof-0.23.28 → flowproof-0.23.30}/Cargo.toml +8 -2
  3. {flowproof-0.23.28 → flowproof-0.23.30}/PKG-INFO +1 -1
  4. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/lib.rs +11 -0
  5. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/web.rs +216 -8
  6. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-agent/src/heal.rs +41 -1
  7. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-agent/src/spec.rs +82 -0
  8. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/src/agent_flow.rs +30 -0
  9. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/src/lib.rs +223 -6
  10. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/agent_conversation_anthropic_e2e.rs +1 -1
  11. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/agent_conversation_streaming_e2e.rs +1 -1
  12. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/agent_flow_e2e.rs +10 -9
  13. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/agent_url_record_e2e.rs +2 -0
  14. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/config_seed_e2e.rs +2 -2
  15. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/egress_e2e.rs +1 -1
  16. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/falsifiability_arg_matchers.rs +1 -0
  17. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/falsifiability_call_order.rs +1 -0
  18. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/falsifiability_mcp_kill.rs +1 -0
  19. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/falsifiability_no_tool_call.rs +1 -0
  20. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/falsifiability_reply_contains.rs +1 -0
  21. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/falsifiability_side_effect.rs +1 -1
  22. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/mcp_http_e2e.rs +1 -0
  23. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/mcp_stdio_e2e.rs +6 -6
  24. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/skip_unless_env.rs +2 -2
  25. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/suite_env_from.rs +6 -2
  26. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/suite_flow_isolation.rs +6 -2
  27. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-driver/Cargo.toml +1 -1
  28. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-python/src/lib.rs +8 -0
  29. {flowproof-0.23.28 → flowproof-0.23.30}/flowproof/__init__.py +1 -1
  30. {flowproof-0.23.28 → flowproof-0.23.30}/pyproject.toml +1 -1
  31. {flowproof-0.23.28 → flowproof-0.23.30}/README.md +0 -0
  32. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/Cargo.toml +0 -0
  33. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/examples/vision_bench.rs +0 -0
  34. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/examples/vision_do.rs +0 -0
  35. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/agent_proxy.rs +0 -0
  36. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/agent_runner.rs +0 -0
  37. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/egress.rs +0 -0
  38. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/egress_linux.rs +0 -0
  39. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/egress_windows/audit.rs +0 -0
  40. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/egress_windows/capture.rs +0 -0
  41. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/egress_windows/env_block.rs +0 -0
  42. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/egress_windows/filters.rs +0 -0
  43. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/egress_windows/identity.rs +0 -0
  44. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/egress_windows/logon.rs +0 -0
  45. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/egress_windows/netevents.rs +0 -0
  46. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/egress_windows/run.rs +0 -0
  47. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/egress_windows/spawn.rs +0 -0
  48. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/egress_windows/wfp.rs +0 -0
  49. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/egress_windows.rs +0 -0
  50. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/fiori.rs +0 -0
  51. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/fs_observe.rs +0 -0
  52. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/mcp_core.rs +0 -0
  53. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/mcp_http.rs +0 -0
  54. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/mcp_stdio.rs +0 -0
  55. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/sap_com.rs +0 -0
  56. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/vision/start.rs +0 -0
  57. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/vision.rs +0 -0
  58. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/vision_bench.rs +0 -0
  59. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/vision_elements.rs +0 -0
  60. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/src/vision_winocr.rs +0 -0
  61. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/tests/a11y_capture.rs +0 -0
  62. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/tests/drag_spike.rs +0 -0
  63. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/tests/fingerprint_capture.rs +0 -0
  64. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/tests/fixtures/windows-start.png +0 -0
  65. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/tests/fs_observation_e2e.rs +0 -0
  66. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/tests/grid_scene_e2e.rs +0 -0
  67. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/tests/list_scene_e2e.rs +0 -0
  68. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/tests/network_idle_e2e.rs +0 -0
  69. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/tests/password_recording_e2e.rs +0 -0
  70. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/tests/shared_browser_shutdown_e2e.rs +0 -0
  71. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/tests/side_effect_http_e2e.rs +0 -0
  72. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/tests/static_area_e2e.rs +0 -0
  73. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/tests/visibility_detach_e2e.rs +0 -0
  74. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-adapters/tests/windows_containment_e2e.rs +0 -0
  75. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-agent/Cargo.toml +0 -0
  76. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-agent/src/agent_steps.rs +0 -0
  77. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-agent/src/author.rs +0 -0
  78. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-agent/src/clarify.rs +0 -0
  79. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-agent/src/code_author.rs +0 -0
  80. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-agent/src/doc_author.rs +0 -0
  81. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-agent/src/doc_formats/hp_alm.rs +0 -0
  82. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-agent/src/doc_formats.rs +0 -0
  83. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-agent/src/draft_assembly.rs +0 -0
  84. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-agent/src/lib.rs +0 -0
  85. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-agent/src/llm.rs +0 -0
  86. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-agent/src/recorder.rs +0 -0
  87. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-agent/src/repair.rs +0 -0
  88. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-agent/src/rules.rs +0 -0
  89. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-agent/tests/model_client_http.rs +0 -0
  90. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/Cargo.toml +0 -0
  91. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/skills/flowproof-config/SKILL.md +0 -0
  92. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/src/agent_surface.rs +0 -0
  93. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/src/capture.rs +0 -0
  94. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/src/config.rs +0 -0
  95. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/src/doctor.rs +0 -0
  96. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/src/main.rs +0 -0
  97. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/src/suite.rs +0 -0
  98. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/src/suite_checkpoint.rs +0 -0
  99. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/src/suite_inputs.rs +0 -0
  100. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/src/suite_journal.rs +0 -0
  101. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/src/update_check.rs +0 -0
  102. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/a11y_selector_e2e.rs +0 -0
  103. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/api_pipeline.rs +0 -0
  104. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/audit_record_e2e.rs +0 -0
  105. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/author_from_code_e2e.rs +0 -0
  106. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/calc_e2e.rs +0 -0
  107. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/citrix_start.rs +0 -0
  108. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/clock_e2e.rs +0 -0
  109. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/config_cli_e2e.rs +0 -0
  110. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/config_skill_e2e.rs +0 -0
  111. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/cookie_e2e.rs +0 -0
  112. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/doctor_ai_e2e.rs +0 -0
  113. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/doctor_e2e.rs +0 -0
  114. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/documented_flags.rs +0 -0
  115. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/examples_resolve.rs +0 -0
  116. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/export_e2e.rs +0 -0
  117. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/falsifiability_audit_since.rs +0 -0
  118. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/falsifiability_capability_regression.rs +0 -0
  119. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/falsifiability_control_verdict.rs +0 -0
  120. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/fiori_field_commit_e2e.rs +0 -0
  121. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/grid_cell_e2e.rs +0 -0
  122. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/heal_apply_proposal.rs +0 -0
  123. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/heal_vars_e2e.rs +0 -0
  124. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/iframe_e2e.rs +0 -0
  125. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/llm_author_e2e.rs +0 -0
  126. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/notepad_author_e2e.rs +0 -0
  127. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/notepad_e2e.rs +0 -0
  128. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/page_title_e2e.rs +0 -0
  129. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/pinned_random_e2e.rs +0 -0
  130. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/record_progress.rs +0 -0
  131. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/sap_e2e.rs +0 -0
  132. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/sap_pipeline.rs +0 -0
  133. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/sap_sim_e2e.rs +0 -0
  134. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/scoped_container_e2e.rs +0 -0
  135. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/select_missing_option_e2e.rs +0 -0
  136. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/sigterm_shared_browser_e2e.rs +0 -0
  137. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/suite_checkpoint.rs +0 -0
  138. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/suite_missing_trace.rs +0 -0
  139. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/suite_policy.rs +0 -0
  140. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/support/sap_simulator.py +0 -0
  141. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/visibility_e2e.rs +0 -0
  142. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/vision_pipeline.rs +0 -0
  143. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/vision_start_replay.rs +0 -0
  144. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-cli/tests/web_e2e.rs +0 -0
  145. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-driver/src/app.rs +0 -0
  146. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-driver/src/backend.rs +0 -0
  147. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-driver/src/gdi.rs +0 -0
  148. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-driver/src/lib.rs +0 -0
  149. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-driver/src/mock.rs +0 -0
  150. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-driver/src/oob.rs +0 -0
  151. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-driver/src/recording.rs +0 -0
  152. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-driver/src/redact.rs +0 -0
  153. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-driver/src/surface.rs +0 -0
  154. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-driver/src/uia_scan.rs +0 -0
  155. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-driver/src/visual.rs +0 -0
  156. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-driver/src/window.rs +0 -0
  157. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-python/Cargo.toml +0 -0
  158. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-replay/Cargo.toml +0 -0
  159. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-replay/src/lib.rs +0 -0
  160. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-replay/src/report.rs +0 -0
  161. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-replay/src/runrecord.rs +0 -0
  162. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-replay/tests/replay_calc.rs +0 -0
  163. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/Cargo.toml +0 -0
  164. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/schema/cassette-v1.schema.json +0 -0
  165. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/schema/side-effect-v1.schema.json +0 -0
  166. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/schema/trace-v1.schema.json +0 -0
  167. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/src/captures.rs +0 -0
  168. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/src/cassette.rs +0 -0
  169. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/src/cassette_diff.rs +0 -0
  170. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/src/egress.rs +0 -0
  171. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/src/format.rs +0 -0
  172. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/src/lib.rs +0 -0
  173. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/src/secret.rs +0 -0
  174. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/src/secret_scan.rs +0 -0
  175. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/src/side_effect.rs +0 -0
  176. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/src/substitution.rs +0 -0
  177. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/src/toolcalls.rs +0 -0
  178. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/src/tosca.rs +0 -0
  179. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/tests/cassette_conformance.rs +0 -0
  180. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/tests/fixtures/guarded.trace.jsonl +0 -0
  181. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/tests/fixtures/multi.trace.jsonl +0 -0
  182. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/tests/fixtures/repeat.trace.jsonl +0 -0
  183. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/tests/fixtures/sample.trace.jsonl +0 -0
  184. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/tests/schema_conformance.rs +0 -0
  185. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/tests/side_effect_conformance.rs +0 -0
  186. {flowproof-0.23.28 → flowproof-0.23.30}/crates/flowproof-trace/tests/tosca_export.rs +0 -0
  187. {flowproof-0.23.28 → flowproof-0.23.30}/flowproof/cli.py +0 -0
  188. {flowproof-0.23.28 → flowproof-0.23.30}/flowproof/flow.py +0 -0
  189. {flowproof-0.23.28 → flowproof-0.23.30}/flowproof/mcp_server.py +0 -0
  190. {flowproof-0.23.28 → flowproof-0.23.30}/flowproof/py.typed +0 -0
@@ -141,15 +141,6 @@ dependencies = [
141
141
  "num-traits",
142
142
  ]
143
143
 
144
- [[package]]
145
- name = "arbitrary"
146
- version = "1.4.2"
147
- source = "registry+https://github.com/rust-lang/crates.io-index"
148
- checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
149
- dependencies = [
150
- "derive_arbitrary",
151
- ]
152
-
153
144
  [[package]]
154
145
  name = "ascii"
155
146
  version = "1.1.0"
@@ -169,11 +160,12 @@ dependencies = [
169
160
 
170
161
  [[package]]
171
162
  name = "atoi_simd"
172
- version = "0.16.1"
163
+ version = "0.18.1"
173
164
  source = "registry+https://github.com/rust-lang/crates.io-index"
174
- checksum = "c2a49e05797ca52e312a0c658938b7d00693ef037799ef7187678f212d7684cf"
165
+ checksum = "f3cdb3708a128e559a30fb830e8a77a5022ee6902806925c216658652b452a44"
175
166
  dependencies = [
176
167
  "debug_unsafe",
168
+ "rustversion",
177
169
  ]
178
170
 
179
171
  [[package]]
@@ -309,9 +301,9 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
309
301
 
310
302
  [[package]]
311
303
  name = "calamine"
312
- version = "0.28.0"
304
+ version = "0.38.0"
313
305
  source = "registry+https://github.com/rust-lang/crates.io-index"
314
- checksum = "15e02a18e79de779a78b0a6ec84a3deed1ff0607dd970a11369f993263f99f1a"
306
+ checksum = "f431b67cba6122a1f7d26b7adafc71bbdf07026095fe811f5efc025d8748e1d8"
315
307
  dependencies = [
316
308
  "atoi_simd",
317
309
  "byteorder",
@@ -628,9 +620,9 @@ dependencies = [
628
620
 
629
621
  [[package]]
630
622
  name = "ctutils"
631
- version = "0.4.2"
623
+ version = "0.4.7"
632
624
  source = "registry+https://github.com/rust-lang/crates.io-index"
633
- checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
625
+ checksum = "a2376ffd6f96d8a9db59875b4b6b0237d6900b5baf64279714e05493ce64828a"
634
626
  dependencies = [
635
627
  "cmov",
636
628
  ]
@@ -688,17 +680,6 @@ version = "0.5.8"
688
680
  source = "registry+https://github.com/rust-lang/crates.io-index"
689
681
  checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
690
682
 
691
- [[package]]
692
- name = "derive_arbitrary"
693
- version = "1.4.2"
694
- source = "registry+https://github.com/rust-lang/crates.io-index"
695
- checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
696
- dependencies = [
697
- "proc-macro2",
698
- "quote",
699
- "syn 2.0.119",
700
- ]
701
-
702
683
  [[package]]
703
684
  name = "derive_builder"
704
685
  version = "0.20.2"
@@ -930,7 +911,7 @@ dependencies = [
930
911
 
931
912
  [[package]]
932
913
  name = "flowproof-adapters"
933
- version = "0.23.28"
914
+ version = "0.23.30"
934
915
  dependencies = [
935
916
  "anyhow",
936
917
  "flowproof-driver",
@@ -949,7 +930,7 @@ dependencies = [
949
930
 
950
931
  [[package]]
951
932
  name = "flowproof-agent"
952
- version = "0.23.28"
933
+ version = "0.23.30"
953
934
  dependencies = [
954
935
  "base64",
955
936
  "chrono",
@@ -959,7 +940,7 @@ dependencies = [
959
940
  "regex",
960
941
  "serde",
961
942
  "serde_json",
962
- "serde_yaml",
943
+ "serde_norway",
963
944
  "thiserror 2.0.18",
964
945
  "tiny_http",
965
946
  "ureq",
@@ -968,7 +949,7 @@ dependencies = [
968
949
 
969
950
  [[package]]
970
951
  name = "flowproof-cli"
971
- version = "0.23.28"
952
+ version = "0.23.30"
972
953
  dependencies = [
973
954
  "ab_glyph",
974
955
  "chrono",
@@ -985,7 +966,7 @@ dependencies = [
985
966
  "rpassword",
986
967
  "serde",
987
968
  "serde_json",
988
- "serde_yaml",
969
+ "serde_norway",
989
970
  "sha2 0.10.9",
990
971
  "tiny_http",
991
972
  "ureq",
@@ -994,7 +975,7 @@ dependencies = [
994
975
 
995
976
  [[package]]
996
977
  name = "flowproof-driver"
997
- version = "0.23.28"
978
+ version = "0.23.30"
998
979
  dependencies = [
999
980
  "calamine",
1000
981
  "flowproof-trace",
@@ -1010,7 +991,7 @@ dependencies = [
1010
991
 
1011
992
  [[package]]
1012
993
  name = "flowproof-python"
1013
- version = "0.23.28"
994
+ version = "0.23.30"
1014
995
  dependencies = [
1015
996
  "flowproof-agent",
1016
997
  "flowproof-cli",
@@ -1022,7 +1003,7 @@ dependencies = [
1022
1003
 
1023
1004
  [[package]]
1024
1005
  name = "flowproof-replay"
1025
- version = "0.23.28"
1006
+ version = "0.23.30"
1026
1007
  dependencies = [
1027
1008
  "chrono",
1028
1009
  "flowproof-agent",
@@ -1036,7 +1017,7 @@ dependencies = [
1036
1017
 
1037
1018
  [[package]]
1038
1019
  name = "flowproof-trace"
1039
- version = "0.23.28"
1020
+ version = "0.23.30"
1040
1021
  dependencies = [
1041
1022
  "jsonschema",
1042
1023
  "regex",
@@ -2169,9 +2150,9 @@ dependencies = [
2169
2150
 
2170
2151
  [[package]]
2171
2152
  name = "quick-xml"
2172
- version = "0.37.5"
2153
+ version = "0.42.0"
2173
2154
  source = "registry+https://github.com/rust-lang/crates.io-index"
2174
- checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
2155
+ checksum = "41b1177fdf999d2321d3fb46ff47159d9c1fb9ad66a4879f8c50a0b504615e9b"
2175
2156
  dependencies = [
2176
2157
  "encoding_rs",
2177
2158
  "memchr",
@@ -2571,9 +2552,9 @@ dependencies = [
2571
2552
 
2572
2553
  [[package]]
2573
2554
  name = "rustls"
2574
- version = "0.23.42"
2555
+ version = "0.23.46"
2575
2556
  source = "registry+https://github.com/rust-lang/crates.io-index"
2576
- checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138"
2557
+ checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf"
2577
2558
  dependencies = [
2578
2559
  "log",
2579
2560
  "once_cell",
@@ -2634,9 +2615,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
2634
2615
 
2635
2616
  [[package]]
2636
2617
  name = "rustls-webpki"
2637
- version = "0.103.13"
2618
+ version = "0.103.16"
2638
2619
  source = "registry+https://github.com/rust-lang/crates.io-index"
2639
- checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
2620
+ checksum = "4443b1b6d39d7c663139942cc4558262562d2c84123bb6282ef177009ca8b281"
2640
2621
  dependencies = [
2641
2622
  "ring",
2642
2623
  "rustls-pki-types",
@@ -2761,16 +2742,16 @@ dependencies = [
2761
2742
  ]
2762
2743
 
2763
2744
  [[package]]
2764
- name = "serde_yaml"
2765
- version = "0.9.34+deprecated"
2745
+ name = "serde_norway"
2746
+ version = "0.9.42"
2766
2747
  source = "registry+https://github.com/rust-lang/crates.io-index"
2767
- checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
2748
+ checksum = "e408f29489b5fd500fab51ff1484fc859bb655f32c671f307dcd733b72e8168c"
2768
2749
  dependencies = [
2769
2750
  "indexmap",
2770
2751
  "itoa",
2771
2752
  "ryu",
2772
2753
  "serde",
2773
- "unsafe-libyaml",
2754
+ "unsafe-libyaml-norway",
2774
2755
  ]
2775
2756
 
2776
2757
  [[package]]
@@ -3142,6 +3123,12 @@ dependencies = [
3142
3123
  "pom",
3143
3124
  ]
3144
3125
 
3126
+ [[package]]
3127
+ name = "typed-path"
3128
+ version = "0.13.0"
3129
+ source = "registry+https://github.com/rust-lang/crates.io-index"
3130
+ checksum = "9ce898823d8cd0f028662d2f349694b1af0a5cc9be83f1d213a00d3147445e1e"
3131
+
3145
3132
  [[package]]
3146
3133
  name = "typeid"
3147
3134
  version = "1.0.3"
@@ -3211,10 +3198,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
3211
3198
  checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
3212
3199
 
3213
3200
  [[package]]
3214
- name = "unsafe-libyaml"
3215
- version = "0.2.11"
3201
+ name = "unsafe-libyaml-norway"
3202
+ version = "0.2.15"
3216
3203
  source = "registry+https://github.com/rust-lang/crates.io-index"
3217
- checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
3204
+ checksum = "b39abd59bf32521c7f2301b52d05a6a2c975b6003521cbd0c6dc1582f0a22104"
3218
3205
 
3219
3206
  [[package]]
3220
3207
  name = "untrusted"
@@ -3420,18 +3407,18 @@ dependencies = [
3420
3407
 
3421
3408
  [[package]]
3422
3409
  name = "webpki-root-certs"
3423
- version = "1.0.9"
3410
+ version = "1.0.11"
3424
3411
  source = "registry+https://github.com/rust-lang/crates.io-index"
3425
- checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b"
3412
+ checksum = "e49d0e65abc00c142e8f9428da991da9f1a250c39be97459901eb12b9b49f62a"
3426
3413
  dependencies = [
3427
3414
  "rustls-pki-types",
3428
3415
  ]
3429
3416
 
3430
3417
  [[package]]
3431
3418
  name = "webpki-roots"
3432
- version = "1.0.9"
3419
+ version = "1.0.11"
3433
3420
  source = "registry+https://github.com/rust-lang/crates.io-index"
3434
- checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a"
3421
+ checksum = "f5f280bbb2b287bc8e7de7e3852945a0ff50d1bbcb5ed42b6c7a568c546df171"
3435
3422
  dependencies = [
3436
3423
  "rustls-pki-types",
3437
3424
  ]
@@ -3890,15 +3877,18 @@ dependencies = [
3890
3877
 
3891
3878
  [[package]]
3892
3879
  name = "zip"
3893
- version = "4.6.1"
3880
+ version = "9.1.3"
3894
3881
  source = "registry+https://github.com/rust-lang/crates.io-index"
3895
- checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1"
3882
+ checksum = "1156b39d142a34b999c2310a50455e0cb3760bf7efad0122f796d813bee26ccb"
3896
3883
  dependencies = [
3897
- "arbitrary",
3898
3884
  "crc32fast",
3899
3885
  "flate2",
3886
+ "getrandom 0.4.3",
3900
3887
  "indexmap",
3901
3888
  "memchr",
3889
+ "nix",
3890
+ "typed-path",
3891
+ "windows-sys 0.61.2",
3902
3892
  "zopfli",
3903
3893
  ]
3904
3894
 
@@ -22,7 +22,7 @@ members = ["crates/flowproof-driver", "crates/flowproof-trace", "crates/flowproo
22
22
  exclude = ["spike/windows-containment"]
23
23
 
24
24
  [workspace.package]
25
- version = "0.23.28"
25
+ version = "0.23.30"
26
26
  edition = "2021"
27
27
  license = "Apache-2.0"
28
28
  repository = "https://github.com/automators-com/flowproof"
@@ -44,7 +44,13 @@ pdf-extract = "0.12"
44
44
  serde = { version = "1", features = ["derive"] }
45
45
  regex = "1"
46
46
  serde_json = "1"
47
- serde_yaml = "0.9"
47
+ # serde_yaml (dtolnay) is archived/unmaintained and pulls the unmaintained
48
+ # unsafe-libyaml (RUSTSEC-2024-0449). serde_norway is the RustSec-recommended
49
+ # maintained 0.9-compatible fork; it rides on the maintained
50
+ # unsafe-libyaml-norway. Aliased back to `serde_yaml` so the ~124 existing
51
+ # `serde_yaml::` call sites stay unchanged — the name is cosmetic, the crate
52
+ # is serde_norway. (A6)
53
+ serde_yaml = { package = "serde_norway", version = "0.9" }
48
54
  thiserror = "2"
49
55
  uuid = { version = "1", features = ["v4"] }
50
56
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flowproof
3
- Version: 0.23.28
3
+ Version: 0.23.30
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: Programming Language :: Python :: 3
@@ -112,6 +112,12 @@ pub use web::shutdown_shared_browser;
112
112
  #[cfg(feature = "web")]
113
113
  pub use web::SharedBrowserGuard;
114
114
 
115
+ /// The words a missing-browser failure starts with. By the time it reaches a
116
+ /// caller it is only a message (the driver, the CLI and the desktop app all
117
+ /// carry it as a string), so these words are what tells it apart from any
118
+ /// other launch failure. Changing them breaks those callers.
119
+ pub const NO_BROWSER: &str = "no browser found";
120
+
115
121
  #[derive(Debug, thiserror::Error)]
116
122
  pub enum AdapterError {
117
123
  #[error("adapter '{0}' is not implemented yet")]
@@ -120,6 +126,11 @@ pub enum AdapterError {
120
126
  UnsupportedPlatform(&'static str),
121
127
  #[error("web adapter: {0}")]
122
128
  Web(String),
129
+ /// No Chromium browser to drive: not a launch that went wrong but one
130
+ /// that could not start, and installing a browser fixes it. Starts with
131
+ /// [`NO_BROWSER`]; the web adapter's tests hold the two together.
132
+ #[error("no browser found: {0}")]
133
+ NoBrowser(String),
123
134
  }
124
135
 
125
136
  /// Names of the adapters compiled into this build.
@@ -4,7 +4,7 @@
4
4
  //!
5
5
  //! Selector mapping: `css` payload key, else `#<automation_id>`. `launch`
6
6
  //! interprets `command` as the URL to open. The Chromium binary is found via
7
- //! the `CHROME` env var or platform auto-detection.
7
+ //! the `CHROME` env var or platform auto-detection ([`find_browser`]).
8
8
 
9
9
  use std::sync::{Arc, Mutex, OnceLock};
10
10
  use std::time::{Duration, Instant};
@@ -897,19 +897,132 @@ fn present_headed_window(window: &impl HeadedTabWindow, headed: bool) -> Result<
897
897
  Ok(())
898
898
  }
899
899
 
900
+ /// What a person reads when no browser is found: which browsers work, and the
901
+ /// two ways to give flowproof one.
902
+ const NO_BROWSER_HELP: &str = "web flows run in Google Chrome, Microsoft Edge, Chromium or \
903
+ Brave, and none is installed where flowproof looks. Install one, or set CHROME to the \
904
+ path of one.";
905
+
906
+ /// The browser to launch. `CHROME` wins when set, and a `CHROME` naming a file
907
+ /// that is not there is an error rather than a quiet fall-back to whatever
908
+ /// else is installed: the person asked for that browser. Otherwise the places
909
+ /// headless_chrome looks, first and unchanged, so nobody's browser changes
910
+ /// under them; then the Chromium browsers it misses.
911
+ fn find_browser() -> Result<std::path::PathBuf, AdapterError> {
912
+ pick_browser(
913
+ std::env::var_os("CHROME"),
914
+ headless_chrome::browser::default_executable,
915
+ &extra_browser_candidates(std::env::consts::OS, std::env::var_os),
916
+ |path| path.is_file(),
917
+ )
918
+ }
919
+
920
+ /// [`find_browser`] with the environment, the default lookup and the
921
+ /// filesystem passed in, so every branch is testable on any host.
922
+ fn pick_browser(
923
+ chrome: Option<std::ffi::OsString>,
924
+ default: impl FnOnce() -> Result<std::path::PathBuf, String>,
925
+ extra: &[std::path::PathBuf],
926
+ exists: impl Fn(&std::path::Path) -> bool,
927
+ ) -> Result<std::path::PathBuf, AdapterError> {
928
+ if let Some(chrome) = chrome.filter(|c| !c.is_empty()) {
929
+ let path = std::path::PathBuf::from(chrome);
930
+ if exists(&path) {
931
+ return Ok(path);
932
+ }
933
+ return Err(AdapterError::NoBrowser(format!(
934
+ "CHROME is set to {}, and there is no browser there. Point it at a Chrome, \
935
+ Edge, Chromium or Brave executable, or unset it to let flowproof look.",
936
+ path.display()
937
+ )));
938
+ }
939
+ if let Ok(path) = default() {
940
+ return Ok(path);
941
+ }
942
+ extra
943
+ .iter()
944
+ .find(|path| exists(path))
945
+ .cloned()
946
+ .ok_or_else(|| AdapterError::NoBrowser(NO_BROWSER_HELP.into()))
947
+ }
948
+
949
+ /// Chromium browsers headless_chrome does not look for: per-user installs of
950
+ /// Chrome, Edge and Chromium (where they land without admin rights) and Brave.
951
+ /// Other Chromium browsers stay out until one is seen driving a flow.
952
+ fn extra_browser_candidates(
953
+ os: &str,
954
+ env: impl Fn(&'static str) -> Option<std::ffi::OsString>,
955
+ ) -> Vec<std::path::PathBuf> {
956
+ let dir = |key: &'static str| {
957
+ env(key)
958
+ .filter(|v| !v.is_empty())
959
+ .map(std::path::PathBuf::from)
960
+ };
961
+ let mut candidates = Vec::new();
962
+ match os {
963
+ "macos" => {
964
+ // The app bundle and its executable share a name for all four.
965
+ let apps = [
966
+ "Google Chrome",
967
+ "Microsoft Edge",
968
+ "Chromium",
969
+ "Brave Browser",
970
+ ];
971
+ let user_apps = dir("HOME").map(|home| home.join("Applications"));
972
+ for root in user_apps
973
+ .into_iter()
974
+ .chain([std::path::PathBuf::from("/Applications")])
975
+ {
976
+ for app in apps {
977
+ candidates.push(root.join(format!("{app}.app/Contents/MacOS/{app}")));
978
+ }
979
+ }
980
+ }
981
+ "windows" => {
982
+ let chrome = ["Google", "Chrome", "Application", "chrome.exe"];
983
+ let edge = ["Microsoft", "Edge", "Application", "msedge.exe"];
984
+ let brave = ["BraveSoftware", "Brave-Browser", "Application", "brave.exe"];
985
+ let installs: [(&str, &[[&str; 4]]); 3] = [
986
+ ("LOCALAPPDATA", &[chrome, edge, brave]),
987
+ ("ProgramFiles", &[chrome, edge, brave]),
988
+ ("ProgramFiles(x86)", &[chrome, brave]),
989
+ ];
990
+ for (key, browsers) in installs {
991
+ let Some(root) = dir(key) else { continue };
992
+ for parts in browsers {
993
+ candidates.push(
994
+ parts
995
+ .iter()
996
+ .fold(root.clone(), |path, part| path.join(part)),
997
+ );
998
+ }
999
+ }
1000
+ }
1001
+ _ => {
1002
+ if let Some(path) = env("PATH") {
1003
+ for bin in std::env::split_paths(&path) {
1004
+ for name in ["brave-browser", "brave"] {
1005
+ candidates.push(bin.join(name));
1006
+ }
1007
+ }
1008
+ }
1009
+ }
1010
+ }
1011
+ candidates
1012
+ }
1013
+
900
1014
  /// Build the launch options, split out from [`launch_browser`] so the headless
901
1015
  /// decision is testable without starting a browser.
902
1016
  fn launch_options_for(
903
1017
  os_args: &[std::ffi::OsString],
904
1018
  headed: bool,
1019
+ path: std::path::PathBuf,
905
1020
  ) -> Result<LaunchOptions<'_>, AdapterError> {
906
1021
  let mut options = LaunchOptions::default_builder();
907
1022
  options.headless(!headed).sandbox(false);
908
1023
  options.idle_browser_timeout(BROWSER_IDLE_TIMEOUT);
909
1024
  options.args(os_args.iter().map(AsRef::as_ref).collect());
910
- if let Ok(path) = std::env::var("CHROME") {
911
- options.path(Some(path.into()));
912
- }
1025
+ options.path(Some(path));
913
1026
  options
914
1027
  .build()
915
1028
  .map_err(|e| AdapterError::Web(format!("building launch options: {e}")))
@@ -1052,7 +1165,7 @@ fn settled_scene(
1052
1165
  fn launch_browser(extra_args: &[String]) -> Result<Browser, AdapterError> {
1053
1166
  let headed = headed_requested();
1054
1167
  let os_args: Vec<std::ffi::OsString> = extra_args.iter().map(Into::into).collect();
1055
- let options = launch_options_for(&os_args, headed)?;
1168
+ let options = launch_options_for(&os_args, headed, find_browser()?)?;
1056
1169
  Browser::new(options)
1057
1170
  .map_err(|e| AdapterError::Web(launch_failure_message(&e.to_string(), headed)))
1058
1171
  }
@@ -5978,14 +6091,108 @@ mod tests {
5978
6091
  #[test]
5979
6092
  fn headed_flips_the_headless_launch_option() {
5980
6093
  let no_args: Vec<std::ffi::OsString> = Vec::new();
6094
+ let chrome = || std::path::PathBuf::from("/opt/chrome");
5981
6095
 
5982
- let headless = super::launch_options_for(&no_args, false).expect("options build");
6096
+ let headless = super::launch_options_for(&no_args, false, chrome()).expect("options build");
5983
6097
  assert!(headless.headless, "default must stay headless");
6098
+ assert_eq!(
6099
+ headless.path,
6100
+ Some(chrome()),
6101
+ "the found browser is the one launched"
6102
+ );
5984
6103
 
5985
- let headed = super::launch_options_for(&no_args, true).expect("options build");
6104
+ let headed = super::launch_options_for(&no_args, true, chrome()).expect("options build");
5986
6105
  assert!(!headed.headless, "FLOWPROOF_HEADED must show the window");
5987
6106
  }
5988
6107
 
6108
+ /// The order the browser is chosen in, and what a person reads when there
6109
+ /// is none. The message must start with `NO_BROWSER`: the CLI and the
6110
+ /// desktop app recognise the failure by those words alone.
6111
+ #[test]
6112
+ fn a_browser_is_chosen_from_chrome_then_the_defaults_then_the_extras() {
6113
+ use std::path::{Path, PathBuf};
6114
+ let found = |p: &Path| p == Path::new("/set/chrome") || p == Path::new("/extra/brave");
6115
+ let default_finds = || -> Result<PathBuf, String> { Ok(PathBuf::from("/default/chrome")) };
6116
+ let default_misses = || -> Result<PathBuf, String> {
6117
+ Err("Could not auto detect a chrome executable".into())
6118
+ };
6119
+ let extras = [
6120
+ PathBuf::from("/extra/missing"),
6121
+ PathBuf::from("/extra/brave"),
6122
+ ];
6123
+
6124
+ let chosen = super::pick_browser(Some("/set/chrome".into()), default_finds, &extras, found);
6125
+ assert_eq!(chosen.expect("CHROME"), PathBuf::from("/set/chrome"));
6126
+
6127
+ // An empty CHROME is unset, not a path.
6128
+ let chosen = super::pick_browser(Some("".into()), default_finds, &extras, found);
6129
+ assert_eq!(chosen.expect("default"), PathBuf::from("/default/chrome"));
6130
+
6131
+ // headless_chrome's own lookup still comes first, so nobody's browser
6132
+ // changes when a second one is installed.
6133
+ let chosen = super::pick_browser(None, default_finds, &extras, found);
6134
+ assert_eq!(chosen.expect("default"), PathBuf::from("/default/chrome"));
6135
+
6136
+ let chosen = super::pick_browser(None, default_misses, &extras, found);
6137
+ assert_eq!(chosen.expect("extra"), PathBuf::from("/extra/brave"));
6138
+
6139
+ let none = super::pick_browser(None, default_misses, &extras[..1], found)
6140
+ .expect_err("nothing installed")
6141
+ .to_string();
6142
+ assert!(none.starts_with(crate::NO_BROWSER), "{none}");
6143
+ assert!(none.contains("set CHROME"), "names the way out: {none}");
6144
+
6145
+ // A CHROME pointing nowhere is the person's choice gone wrong: say so
6146
+ // rather than quietly launching some other browser.
6147
+ let wrong = super::pick_browser(Some("/gone/chrome".into()), default_finds, &extras, found)
6148
+ .expect_err("CHROME names nothing")
6149
+ .to_string();
6150
+ assert!(wrong.starts_with(crate::NO_BROWSER), "{wrong}");
6151
+ assert!(
6152
+ wrong.contains("/gone/chrome"),
6153
+ "names the path it tried: {wrong}"
6154
+ );
6155
+ }
6156
+
6157
+ /// The per-user installs are the point: Chrome and Edge installed without
6158
+ /// admin rights land there, and headless_chrome does not look.
6159
+ #[test]
6160
+ fn extra_candidates_cover_per_user_installs_and_brave() {
6161
+ use std::ffi::OsString;
6162
+ let env = |key: &str| -> Option<OsString> {
6163
+ match key {
6164
+ "HOME" => Some("/Users/ana".into()),
6165
+ "LOCALAPPDATA" => Some("C:\\Users\\ana\\AppData\\Local".into()),
6166
+ "PATH" => Some("/usr/bin".into()),
6167
+ _ => None,
6168
+ }
6169
+ };
6170
+ let shown = |os: &str| -> Vec<String> {
6171
+ super::extra_browser_candidates(os, env)
6172
+ .iter()
6173
+ .map(|p| p.to_string_lossy().replace('\\', "/"))
6174
+ .collect()
6175
+ };
6176
+
6177
+ let mac = shown("macos");
6178
+ assert!(mac.contains(
6179
+ &"/Users/ana/Applications/Google Chrome.app/Contents/MacOS/Google Chrome".to_string()
6180
+ ));
6181
+ assert!(mac
6182
+ .contains(&"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser".to_string()));
6183
+
6184
+ let windows = shown("windows");
6185
+ assert!(windows.contains(
6186
+ &"C:/Users/ana/AppData/Local/Google/Chrome/Application/chrome.exe".to_string()
6187
+ ));
6188
+ assert!(
6189
+ windows.iter().all(|p| p.starts_with("C:/Users/ana")),
6190
+ "an unset ProgramFiles adds nothing: {windows:?}"
6191
+ );
6192
+
6193
+ assert_eq!(shown("linux"), ["/usr/bin/brave-browser", "/usr/bin/brave"]);
6194
+ }
6195
+
5989
6196
  /// A visible shared browser leaks its keep-alive window into the user's
5990
6197
  /// desktop after the flow tab closes. Headless suites retain reuse; either
5991
6198
  /// explicit opt-out or headed mode must select one privately-owned process.
@@ -6259,7 +6466,8 @@ mod startup_tab_tests {
6259
6466
  return;
6260
6467
  }
6261
6468
  let browser = super::Browser::new(
6262
- super::launch_options_for(&[], true).expect("valid launch options"),
6469
+ super::launch_options_for(&[], true, super::find_browser().expect("a browser"))
6470
+ .expect("valid launch options"),
6263
6471
  )
6264
6472
  .expect("browser starts");
6265
6473
  let first = super::private_flow_tab(&browser).expect("flow tab opens");
@@ -6,7 +6,7 @@
6
6
  //! original as `<name>.proposed.jsonl` and is only applied on explicit
7
7
  //! request.
8
8
 
9
- use std::path::{Path, PathBuf};
9
+ use std::path::{Component, Path, PathBuf};
10
10
 
11
11
  use flowproof_driver::AppDriver;
12
12
  use flowproof_trace::format::{Header, Step};
@@ -308,6 +308,19 @@ fn remap_selector_refs(value: &mut serde_json::Value, moved: &dyn Fn(usize) -> u
308
308
  }
309
309
  }
310
310
 
311
+ /// A recording-bundle path from a trace header is trusted only when it stays a
312
+ /// relative path inside the trace directory: every component must be `Normal`
313
+ /// (which rejects an absolute root, `.`, and `..`) and it must contain no `\`,
314
+ /// a path separator on Windows that would otherwise traverse there. Mirrors the
315
+ /// CLI's suite-selection guard (`relative_path`).
316
+ fn is_confined_rel(dir: &str) -> bool {
317
+ !dir.is_empty()
318
+ && !dir.contains('\\')
319
+ && Path::new(dir)
320
+ .components()
321
+ .all(|c| matches!(c, Component::Normal(_)))
322
+ }
323
+
311
324
  /// The step time-range → frame files of one execution's recording bundle.
312
325
  /// Derivable purely from the structured data plus the content-named files.
313
326
  fn frames_for_range(
@@ -321,6 +334,15 @@ fn frames_for_range(
321
334
  let Some(recording) = &header.recording else {
322
335
  return Vec::new();
323
336
  };
337
+ // `recording.dir` comes from the trace header, which may be attacker-
338
+ // authored (an imported trace). Confine it to a child of `trace_dir` before
339
+ // reading the bundle or embedding its files into the diff HTML: an absolute
340
+ // path, a `..`, or a `\` (a separator on Windows) would otherwise point the
341
+ // page at files outside the recording. A bad value yields no frames rather
342
+ // than reaching out of the tree.
343
+ if !is_confined_rel(&recording.dir) {
344
+ return Vec::new();
345
+ }
324
346
  let dir = trace_dir.join(&recording.dir);
325
347
  let Ok(entries) = std::fs::read_dir(&dir) else {
326
348
  return Vec::new();
@@ -485,6 +507,24 @@ steps:
485
507
  MockAppDriver::new(&CALC_ELEMENTS).with_text("CalculatorResults", "Display is 8")
486
508
  }
487
509
 
510
+ #[test]
511
+ fn recording_dir_is_confined_to_the_trace_tree() {
512
+ // Legitimate bundle paths: relative, forward-slashed, no traversal.
513
+ assert!(is_confined_rel("recording"));
514
+ assert!(is_confined_rel("exec-0/recording"));
515
+
516
+ // A header must not point the diff page outside the trace directory.
517
+ assert!(!is_confined_rel(""), "empty");
518
+ assert!(!is_confined_rel("../recording"), "parent traversal");
519
+ assert!(!is_confined_rel("a/../../etc"), "nested traversal");
520
+ assert!(!is_confined_rel("/etc/ssh"), "absolute (unix)");
521
+ assert!(!is_confined_rel("..\\recording"), "backslash traversal");
522
+ assert!(
523
+ !is_confined_rel("C:\\Windows"),
524
+ "backslash catches a Windows absolute path on any host"
525
+ );
526
+ }
527
+
488
528
  #[test]
489
529
  fn healthy_trace_needs_no_healing() {
490
530
  let dir = std::env::temp_dir().join("flowproof-heal-healthy");