unique_web_search 2026.22.0.dev2__tar.gz → 2026.22.0.dev3__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 (118) hide show
  1. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/PKG-INFO +2 -2
  2. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/pyproject.toml +2 -2
  3. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/crawl4ai.py +42 -37
  4. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/README.md +0 -0
  5. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/__init__.py +0 -0
  6. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/client_settings.py +0 -0
  7. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/config.py +0 -0
  8. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/metrics.py +0 -0
  9. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/prompts.py +0 -0
  10. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/schema.py +0 -0
  11. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/service.py +0 -0
  12. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/argument_screening/__init__.py +0 -0
  13. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/argument_screening/config.py +0 -0
  14. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/argument_screening/exceptions.py +0 -0
  15. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/argument_screening/prompts/__init__.py +0 -0
  16. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/argument_screening/prompts/guidelines.j2 +0 -0
  17. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/argument_screening/prompts/rejection_response.j2 +0 -0
  18. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/argument_screening/prompts/system_prompt.j2 +0 -0
  19. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/argument_screening/prompts/user_prompt.j2 +0 -0
  20. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/argument_screening/service.py +0 -0
  21. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/client/proxy_config.py +0 -0
  22. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/__init__.py +0 -0
  23. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/cleaning/__init__.py +0 -0
  24. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/cleaning/character_sanitize.py +0 -0
  25. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/cleaning/clean.py +0 -0
  26. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/cleaning/config.py +0 -0
  27. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/config.py +0 -0
  28. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/processing_strategies/base.py +0 -0
  29. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/processing_strategies/config.py +0 -0
  30. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/processing_strategies/llm_guard_judge.py +0 -0
  31. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/processing_strategies/llm_keyword_redact.py +0 -0
  32. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/processing_strategies/llm_process.py +0 -0
  33. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/processing_strategies/prompts/__init__.py +0 -0
  34. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/processing_strategies/prompts/judge_and_sanitize_prompt.j2 +0 -0
  35. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/processing_strategies/prompts/judge_prompt.j2 +0 -0
  36. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/processing_strategies/prompts/keyword_extract_prompt.j2 +0 -0
  37. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/processing_strategies/prompts/page_context.j2 +0 -0
  38. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/processing_strategies/prompts/sanitize_rules.j2 +0 -0
  39. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/processing_strategies/prompts/system_prompt.j2 +0 -0
  40. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/processing_strategies/prompts/user_prompt.j2 +0 -0
  41. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/processing_strategies/schema.py +0 -0
  42. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/processing_strategies/settings.py +0 -0
  43. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/processing_strategies/truncate.py +0 -0
  44. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/content_processing/service.py +0 -0
  45. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/README.md +0 -0
  46. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/__init__.py +0 -0
  47. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/base.py +0 -0
  48. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/basic.py +0 -0
  49. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/firecrawl.py +0 -0
  50. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/jina.py +0 -0
  51. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/tavily.py +0 -0
  52. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/utils.py +0 -0
  53. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/README.md +0 -0
  54. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/__init__.py +0 -0
  55. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/base_config.py +0 -0
  56. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/base_executor.py +0 -0
  57. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/context.py +0 -0
  58. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v1/__init__.py +0 -0
  59. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v1/config.py +0 -0
  60. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v1/executor.py +0 -0
  61. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v1/prompts/__init__.py +0 -0
  62. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v1/prompts/refine_query_system_prompt.j2 +0 -0
  63. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v1/prompts/restrict_date_description.j2 +0 -0
  64. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v1/prompts/tool_description.j2 +0 -0
  65. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v1/prompts/tool_description_for_system_prompt.j2 +0 -0
  66. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v1/schema.py +0 -0
  67. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v2/__init__.py +0 -0
  68. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v2/config.py +0 -0
  69. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v2/executor.py +0 -0
  70. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v2/prompts/__init__.py +0 -0
  71. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v2/prompts/step_query_description.j2 +0 -0
  72. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v2/prompts/tool_description.j2 +0 -0
  73. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v2/prompts/tool_description_for_system_prompt.j2 +0 -0
  74. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v2/schema.py +0 -0
  75. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v3/__init__.py +0 -0
  76. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v3/config.py +0 -0
  77. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v3/executor.py +0 -0
  78. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v3/prompts/__init__.py +0 -0
  79. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v3/prompts/tool_description.j2 +0 -0
  80. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v3/prompts/tool_description_for_system_prompt.j2 +0 -0
  81. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/v3/schema.py +0 -0
  82. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/helpers.py +0 -0
  83. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/message_log.py +0 -0
  84. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/query_elicitation.py +0 -0
  85. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/README.md +0 -0
  86. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/__init__.py +0 -0
  87. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/base.py +0 -0
  88. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/bing.py +0 -0
  89. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/brave.py +0 -0
  90. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/custom_api.py +0 -0
  91. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/firecrawl.py +0 -0
  92. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/google.py +0 -0
  93. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/jina.py +0 -0
  94. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/schema.py +0 -0
  95. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/tavily.py +0 -0
  96. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/utils/google/schema.py +0 -0
  97. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/utils/grounding/__init__.py +0 -0
  98. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/utils/grounding/bing/__init__.py +0 -0
  99. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/utils/grounding/bing/client.py +0 -0
  100. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/utils/grounding/bing/models.py +0 -0
  101. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/utils/grounding/bing/runner.py +0 -0
  102. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/utils/grounding/models.py +0 -0
  103. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/utils/grounding/response_parsing.py +0 -0
  104. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/__init__.py +0 -0
  105. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/client.py +0 -0
  106. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/exceptions.py +0 -0
  107. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/gemini.py +0 -0
  108. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/utils/grounding/vertexai/response_handler.py +0 -0
  109. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/search_engine/vertexai.py +0 -0
  110. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/snippet_judge/__init__.py +0 -0
  111. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/snippet_judge/prompts.py +0 -0
  112. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/snippet_judge/schema.py +0 -0
  113. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/snippet_judge/service.py +0 -0
  114. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/structured_llm.py +0 -0
  115. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/url_safety.py +0 -0
  116. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/settings.py +0 -0
  117. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/skills/web-search-v3.md +0 -0
  118. {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: unique-web-search
3
- Version: 2026.22.0.dev2
3
+ Version: 2026.22.0.dev3
4
4
  Summary:
5
5
  Author: Andreas Hauri, Gustav Hartz, Rami Azouz
6
6
  Author-email: Andreas Hauri <andreas@unique.ch>, Gustav Hartz <gustav.hartz.ext@unique.ch>, Rami Azouz <rami.ext@unique.ch>
@@ -17,7 +17,7 @@ Requires-Dist: tavily-python>=0.7.11,<1
17
17
  Requires-Dist: unidecode>=1.4.0,<2
18
18
  Requires-Dist: azure-ai-projects>=1.0.0,<2
19
19
  Requires-Dist: azure-identity>=1.25.0,<2
20
- Requires-Dist: unique-toolkit[monitoring]>=2026.22.0.dev2,<2026.22.0rc0
20
+ Requires-Dist: unique-toolkit[monitoring]>=2026.22.0.dev3,<2026.22.0rc0
21
21
  Requires-Dist: azure-core>=1.36.0,<2
22
22
  Requires-Dist: google-cloud-aiplatform>=1.128.0,<2
23
23
  Requires-Dist: google-auth>=2.43.0,<3
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "unique_web_search"
3
- version = "2026.22.0.dev2"
3
+ version = "2026.22.0.dev3"
4
4
  description = ""
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12,<4.0"
@@ -23,7 +23,7 @@ dependencies = [
23
23
  "unidecode>=1.4.0,<2",
24
24
  "azure-ai-projects>=1.0.0,<2",
25
25
  "azure-identity>=1.25.0,<2",
26
- "unique-toolkit[monitoring]>=2026.22.0.dev2,<2026.22.0rc0",
26
+ "unique-toolkit[monitoring]>=2026.22.0.dev3,<2026.22.0rc0",
27
27
  "azure-core>=1.36.0,<2",
28
28
  "google-cloud-aiplatform>=1.128.0,<2",
29
29
  "google-auth>=2.43.0,<3",
@@ -2,8 +2,6 @@ import logging
2
2
  from enum import StrEnum
3
3
  from typing import Any, Literal
4
4
 
5
- from crawl4ai.async_webcrawler import AsyncPlaywrightCrawlerStrategy
6
- from playwright.async_api import Page, Request, Route
7
5
  from pydantic import BaseModel, Field
8
6
  from unique_toolkit.agentic.tools.config import get_configuration_dict
9
7
 
@@ -18,40 +16,6 @@ from unique_web_search.services.url_safety import validate_url
18
16
  _LOGGER = logging.getLogger(__name__)
19
17
 
20
18
 
21
- async def _ssrf_guard_hook(page: Page, **kwargs: object) -> None:
22
- """Playwright ``before_goto`` hook that installs a route interceptor.
23
-
24
- Registers a ``page.route`` handler that validates **every** network request
25
- Chromium makes (navigation, sub-resources, JS ``fetch``) against the shared
26
- url_safety rules. Requests to internal/private targets are aborted before
27
- any TCP connection is opened, closing bypass vectors A–D and sub-resource
28
- SSRF that pre-flight URL validation cannot see.
29
-
30
- crawl4ai calls this hook as::
31
-
32
- hook(page, context=context, url=url, config=config)
33
-
34
- so all arguments after ``page`` are captured via ``**kwargs``.
35
- """
36
-
37
- async def _route_handler(route: Route, request: Request) -> None:
38
- req_url = request.url
39
- error = await validate_url(req_url)
40
- if error is not None:
41
- category, reason = error
42
- _LOGGER.warning(
43
- "Crawl4AI blocked internal request: %s — %s (%s)",
44
- req_url,
45
- reason,
46
- category,
47
- )
48
- await route.abort("blockedbyclient")
49
- return
50
- await route.continue_()
51
-
52
- await page.route("**/*", _route_handler)
53
-
54
-
55
19
  class DisplayMode(StrEnum):
56
20
  DETAILED = "DETAILED"
57
21
  AGGREGATED = "AGGREGATED"
@@ -201,6 +165,46 @@ class Crawl4AiCrawler(BaseCrawler[Crawl4AiCrawlerConfig]):
201
165
  # @track(
202
166
  # tags=["crawl4ai", "scrape"],
203
167
  # )
168
+
169
+ @staticmethod
170
+ def _get_ssrf_guard_hook():
171
+ from playwright.async_api import Page, Request, Route
172
+
173
+ async def _ssrf_guard_hook(page: Page, **kwargs: object) -> None:
174
+ """Playwright ``before_goto`` hook that installs a route interceptor.
175
+
176
+ Registers a ``page.route`` handler that validates **every** network request
177
+ Chromium makes (navigation, sub-resources, JS ``fetch``) against the shared
178
+ url_safety rules. Requests to internal/private targets are aborted before
179
+ any TCP connection is opened, closing bypass vectors A–D and sub-resource
180
+ SSRF that pre-flight URL validation cannot see.
181
+
182
+ crawl4ai calls this hook as::
183
+
184
+ hook(page, context=context, url=url, config=config)
185
+
186
+ so all arguments after ``page`` are captured via ``**kwargs``.
187
+ """
188
+
189
+ async def _route_handler(route: Route, request: Request) -> None:
190
+ req_url = request.url
191
+ error = await validate_url(req_url)
192
+ if error is not None:
193
+ category, reason = error
194
+ _LOGGER.warning(
195
+ "Crawl4AI blocked internal request: %s — %s (%s)",
196
+ req_url,
197
+ reason,
198
+ category,
199
+ )
200
+ await route.abort("blockedbyclient")
201
+ return
202
+ await route.continue_()
203
+
204
+ await page.route("**/*", _route_handler)
205
+
206
+ return _ssrf_guard_hook
207
+
204
208
  async def _crawl(self, urls: list[str]) -> list[str]:
205
209
  # Lazy import of crawl4ai - only import when actually needed
206
210
  from crawl4ai import (
@@ -214,6 +218,7 @@ class Crawl4AiCrawler(BaseCrawler[Crawl4AiCrawlerConfig]):
214
218
  from crawl4ai.async_dispatcher import (
215
219
  SemaphoreDispatcher,
216
220
  )
221
+ from crawl4ai.async_webcrawler import AsyncPlaywrightCrawlerStrategy
217
222
  from crawl4ai.content_filter_strategy import PruningContentFilter
218
223
  from crawl4ai.markdown_generation_strategy import (
219
224
  DefaultMarkdownGenerator,
@@ -274,7 +279,7 @@ class Crawl4AiCrawler(BaseCrawler[Crawl4AiCrawlerConfig]):
274
279
  crawler_strategy = AsyncPlaywrightCrawlerStrategy(
275
280
  browser_config=browser_config,
276
281
  hooks={
277
- "before_goto": _ssrf_guard_hook,
282
+ "before_goto": self._get_ssrf_guard_hook(),
278
283
  },
279
284
  )
280
285