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.
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/PKG-INFO +2 -2
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/pyproject.toml +2 -2
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/crawl4ai.py +42 -37
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/README.md +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/__init__.py +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/client_settings.py +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/config.py +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/metrics.py +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/prompts.py +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/schema.py +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/service.py +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/README.md +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/__init__.py +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/base.py +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/basic.py +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/firecrawl.py +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/jina.py +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/tavily.py +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/crawlers/utils.py +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/README.md +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/__init__.py +0 -0
- {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
- {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
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/executors/context.py +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/helpers.py +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/message_log.py +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/query_elicitation.py +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/structured_llm.py +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/services/url_safety.py +0 -0
- {unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/settings.py +0 -0
- {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
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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":
|
|
282
|
+
"before_goto": self._get_ssrf_guard_hook(),
|
|
278
283
|
},
|
|
279
284
|
)
|
|
280
285
|
|
|
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
|
{unique_web_search-2026.22.0.dev2 → unique_web_search-2026.22.0.dev3}/src/unique_web_search/utils.py
RENAMED
|
File without changes
|