mirror of
https://github.com/langgenius/dify.git
synced 2026-05-09 21:28:25 +08:00
fix: normalize signed call depth for root webhook URLs
This commit is contained in:
parent
4ecba5858b
commit
b8cedefd7d
@ -290,8 +290,10 @@ class Executor:
|
||||
|
||||
Reserved workflow call-depth headers are removed case-insensitively
|
||||
before the canonical pair is re-added from ``workflow_call_depth``.
|
||||
This keeps propagation deterministic even if a workflow author manually
|
||||
configured colliding headers on the node.
|
||||
The signature path mirrors Flask request matching, so URLs without an
|
||||
explicit path are normalized to ``/`` before signing. This keeps
|
||||
propagation deterministic even if a workflow author manually configured
|
||||
colliding headers on the node.
|
||||
"""
|
||||
authorization = deepcopy(self.auth)
|
||||
headers = deepcopy(self.headers) or {}
|
||||
@ -301,12 +303,13 @@ class Executor:
|
||||
}
|
||||
headers = {k: v for k, v in headers.items() if k.lower() not in reserved_header_names}
|
||||
parsed_url = urlparse(self.url)
|
||||
signed_path = parsed_url.path or "/"
|
||||
next_call_depth = str(self.workflow_call_depth + 1)
|
||||
headers[WORKFLOW_CALL_DEPTH_HEADER] = next_call_depth
|
||||
headers[WORKFLOW_CALL_DEPTH_SIGNATURE_HEADER] = build_workflow_call_depth_signature(
|
||||
secret_key=self._http_request_config.secret_key,
|
||||
method=self.method,
|
||||
path=parsed_url.path,
|
||||
path=signed_path,
|
||||
depth=next_call_depth,
|
||||
)
|
||||
|
||||
|
||||
@ -100,6 +100,7 @@ def test_run_uses_single_node_execution_branch(
|
||||
workflow=workflow,
|
||||
single_iteration_run=single_iteration_run,
|
||||
single_loop_run=single_loop_run,
|
||||
call_depth=0,
|
||||
)
|
||||
init_graph.assert_not_called()
|
||||
|
||||
@ -156,6 +157,7 @@ def test_single_node_run_validates_target_node_config(monkeypatch) -> None:
|
||||
node_id="loop-node",
|
||||
user_inputs={},
|
||||
graph_runtime_state=graph_runtime_state,
|
||||
call_depth=0,
|
||||
node_type_filter_key="loop_id",
|
||||
node_type_label="loop",
|
||||
)
|
||||
|
||||
@ -937,3 +937,48 @@ def test_executor_propagates_workflow_call_depth_to_arbitrary_target_with_secret
|
||||
path="/data",
|
||||
depth="3",
|
||||
)
|
||||
|
||||
|
||||
def test_executor_normalizes_empty_url_path_when_signing_workflow_call_depth():
|
||||
variable_pool = VariablePool(
|
||||
system_variables=SystemVariable.default(),
|
||||
user_inputs={},
|
||||
)
|
||||
node_data = HttpRequestNodeData(
|
||||
title="External target without explicit path",
|
||||
method="get",
|
||||
url="https://api.example.com",
|
||||
authorization=HttpRequestNodeAuthorization(type="no-auth"),
|
||||
headers="X-Test: value",
|
||||
params="",
|
||||
)
|
||||
|
||||
executor = Executor(
|
||||
node_data=node_data,
|
||||
timeout=HttpRequestNodeTimeout(connect=10, read=30, write=30),
|
||||
http_request_config=HttpRequestNodeConfig(
|
||||
max_connect_timeout=HTTP_REQUEST_CONFIG.max_connect_timeout,
|
||||
max_read_timeout=HTTP_REQUEST_CONFIG.max_read_timeout,
|
||||
max_write_timeout=HTTP_REQUEST_CONFIG.max_write_timeout,
|
||||
max_binary_size=HTTP_REQUEST_CONFIG.max_binary_size,
|
||||
max_text_size=HTTP_REQUEST_CONFIG.max_text_size,
|
||||
ssl_verify=HTTP_REQUEST_CONFIG.ssl_verify,
|
||||
ssrf_default_max_retries=HTTP_REQUEST_CONFIG.ssrf_default_max_retries,
|
||||
secret_key=TEST_SECRET_KEY,
|
||||
),
|
||||
variable_pool=variable_pool,
|
||||
workflow_call_depth=2,
|
||||
http_client=ssrf_proxy,
|
||||
file_manager=file_manager,
|
||||
)
|
||||
|
||||
headers = executor._assembling_headers()
|
||||
|
||||
assert headers["X-Test"] == "value"
|
||||
assert headers[WORKFLOW_CALL_DEPTH_HEADER] == "3"
|
||||
assert headers[WORKFLOW_CALL_DEPTH_SIGNATURE_HEADER] == build_workflow_call_depth_signature(
|
||||
secret_key=TEST_SECRET_KEY,
|
||||
method="get",
|
||||
path="/",
|
||||
depth="3",
|
||||
)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user