From 3b47fbbee5752420fa91c1a5546ffd49e7394d32 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Tue, 15 Sep 2026 19:25:29 -0700 Subject: [PATCH 1/4] GH-23686: Add regression tests for sensitive headers --- ext/soap/tests/gh23686/check_headers.inc | 101 ++++++++++++++++ .../tests/gh23686/header-from-stringable.phpt | 108 ++++++++++++++++++ .../import-authorization-basic-lowercase.phpt | 41 +++++++ .../gh23686/import-authorization-basic.phpt | 39 +++++++ .../gh23686/import-authorization-bearer.phpt | 41 +++++++ .../gh23686/import-authorization-digest.phpt | 41 +++++++ ext/soap/tests/gh23686/import-cookies.phpt | 41 +++++++ .../gh23686/import-proxy-authorization.phpt | 41 +++++++ .../import-repeated-authorization.phpt | 44 +++++++ .../gh23686/import-repeated-cookies.phpt | 44 +++++++ .../gh23686/import-repeated-proxy-auth.phpt | 44 +++++++ .../schema-authorization-basic-lowercase.phpt | 41 +++++++ .../gh23686/schema-authorization-basic.phpt | 39 +++++++ .../gh23686/schema-authorization-bearer.phpt | 41 +++++++ .../gh23686/schema-authorization-digest.phpt | 41 +++++++ ext/soap/tests/gh23686/schema-cookies.phpt | 41 +++++++ .../gh23686/schema-proxy-authorization.phpt | 41 +++++++ .../schema-repeated-authorization.phpt | 42 +++++++ .../gh23686/schema-repeated-cookies.phpt | 42 +++++++ .../gh23686/schema-repeated-proxy-auth.phpt | 42 +++++++ .../gh23686/schema-respond-with-headers.inc | 9 ++ .../gh23686/soap-respond-with-headers.inc | 16 +++ ext/soap/tests/gh23686/wsdl-with-import.wsdl | 7 ++ ext/soap/tests/gh23686/wsdl-with-schema.wsdl | 19 +++ sapi/cli/tests/php_cli_server.inc | 3 +- 25 files changed, 1008 insertions(+), 1 deletion(-) create mode 100644 ext/soap/tests/gh23686/check_headers.inc create mode 100644 ext/soap/tests/gh23686/header-from-stringable.phpt create mode 100644 ext/soap/tests/gh23686/import-authorization-basic-lowercase.phpt create mode 100644 ext/soap/tests/gh23686/import-authorization-basic.phpt create mode 100644 ext/soap/tests/gh23686/import-authorization-bearer.phpt create mode 100644 ext/soap/tests/gh23686/import-authorization-digest.phpt create mode 100644 ext/soap/tests/gh23686/import-cookies.phpt create mode 100644 ext/soap/tests/gh23686/import-proxy-authorization.phpt create mode 100644 ext/soap/tests/gh23686/import-repeated-authorization.phpt create mode 100644 ext/soap/tests/gh23686/import-repeated-cookies.phpt create mode 100644 ext/soap/tests/gh23686/import-repeated-proxy-auth.phpt create mode 100644 ext/soap/tests/gh23686/schema-authorization-basic-lowercase.phpt create mode 100644 ext/soap/tests/gh23686/schema-authorization-basic.phpt create mode 100644 ext/soap/tests/gh23686/schema-authorization-bearer.phpt create mode 100644 ext/soap/tests/gh23686/schema-authorization-digest.phpt create mode 100644 ext/soap/tests/gh23686/schema-cookies.phpt create mode 100644 ext/soap/tests/gh23686/schema-proxy-authorization.phpt create mode 100644 ext/soap/tests/gh23686/schema-repeated-authorization.phpt create mode 100644 ext/soap/tests/gh23686/schema-repeated-cookies.phpt create mode 100644 ext/soap/tests/gh23686/schema-repeated-proxy-auth.phpt create mode 100644 ext/soap/tests/gh23686/schema-respond-with-headers.inc create mode 100644 ext/soap/tests/gh23686/soap-respond-with-headers.inc create mode 100644 ext/soap/tests/gh23686/wsdl-with-import.wsdl create mode 100644 ext/soap/tests/gh23686/wsdl-with-schema.wsdl diff --git a/ext/soap/tests/gh23686/check_headers.inc b/ext/soap/tests/gh23686/check_headers.inc new file mode 100644 index 000000000000..bc2694d8daff --- /dev/null +++ b/ext/soap/tests/gh23686/check_headers.inc @@ -0,0 +1,101 @@ + switches to a different server, check the headers that the + * second server recieves + */ +function check_headers_for_import(string $logs_path, $context, bool $verbose_failure = true) { + $code = file_get_contents(__DIR__ . "/soap-respond-with-headers.inc"); + $code = substr($code, strpos($code, "port, $wsdl); + + // We cannot just load the SoapClient directly from the WSDL because + // otherwise the credentials are not stripped because we are not switching + // to another host in the . + $server_code = "echo <<port; + + foreach ([SOAP_1_1, SOAP_1_2] as $version) { + try { + $client = new SoapClient("http://localhost:$wsdl_server_port", [ + 'soap_version' => $version, + 'stream_context' => $context, + ]); + } catch (SoapFault $e) { + if ($verbose_failure) { + var_dump($e); + echo "--- Raw Server Response Body ---\n"; + var_dump(file_get_contents("http://localhost:$wsdl_server_port", false, $context)); + } else { + echo get_class($e) . ': ' . $e->getMessage() . "\n"; + } + } + + var_dump(@file_get_contents($logs_path)); + } +} + +/** + * When an leads to a on a different server, check the + * headers that the *third* server recieves + */ +function check_headers_for_schema(string $logs_path, $context, bool $verbose_failure = true) { + $code = file_get_contents(__DIR__ . "/schema-respond-with-headers.inc"); + $code = substr($code, strpos($code, "port, $schema_wsdl); + $schema_server_code = "echo <<port; + + $import_wsdl = file_get_contents(__DIR__ . "/wsdl-with-import.wsdl"); + $import_wsdl = str_replace("{TARGET_PORT}", $schema_wsdl_server_port, $import_wsdl); + $import_server_code = "echo <<port; + + foreach ([SOAP_1_1, SOAP_1_2] as $version) { + try { + $client = new SoapClient("http://localhost:$import_wsdl_server_port", [ + 'soap_version' => $version, + 'stream_context' => $context, + ]); + } catch (SoapFault $e) { + if ($verbose_failure) { + var_dump($e); + echo "--- Raw Server Response Body ---\n"; + var_dump(file_get_contents("http://localhost:$wsdl_server_port", false, $context)); + } else { + echo get_class($e) . ': ' . $e->getMessage() . "\n"; + } + } + + var_dump(@file_get_contents($logs_path)); + } +} diff --git a/ext/soap/tests/gh23686/header-from-stringable.phpt b/ext/soap/tests/gh23686/header-from-stringable.phpt new file mode 100644 index 000000000000..1fe5c36564b5 --- /dev/null +++ b/ext/soap/tests/gh23686/header-from-stringable.phpt @@ -0,0 +1,108 @@ +--TEST-- +GH-23686: Confirm that headers set with stringable objects are ignored +--DESCRIPTION-- +If these objects were converted to strings and the headers used, then they might +need to be redacted, but php_stream_url_wrap_http_ex() only cares about strings +and string elements in arrays as of PHP 8.6. This test means that if that were +to ever change, this test would fail and developers would know to update +the redaction logic in the soap extension. +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- +val; } +} + +// Headers reach sdl_set_uri_credentials() as a string +$context = stream_context_create([ + 'http' => ['header' => [ + new StringWrapper("Authorization: Foo"), + new StringWrapper("Bar: Baz"), + "X-non-object: value", + ]], +]); + +echo "Headers are in a string:\n"; +check_headers_for_import(LOGS_PATH, $context); +check_headers_for_schema(LOGS_PATH, $context); + +// Headers reach sdl_set_uri_credentials() as an array +$context = stream_context_create([ + 'http' => [ + // having a protocol here means that SOAP won't add it itself + 'protocol_version' => 1.1, + 'header' => [ + new StringWrapper("Authorization: Foo"), + new StringWrapper("Bar: Baz"), + "X-non-object: value", + ] + ], +]); + +echo "Headers are in an array:\n"; +check_headers_for_import(LOGS_PATH, $context); +check_headers_for_schema(LOGS_PATH, $context); + +?> +--EXPECTF-- +Headers are in a string: +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'X-non-object' => 'value', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'X-non-object' => 'value', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'X-non-object' => 'value', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'X-non-object' => 'value', +)" +Headers are in an array: +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'X-non-object' => 'value', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'X-non-object' => 'value', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'X-non-object' => 'value', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'X-non-object' => 'value', +)" diff --git a/ext/soap/tests/gh23686/import-authorization-basic-lowercase.phpt b/ext/soap/tests/gh23686/import-authorization-basic-lowercase.phpt new file mode 100644 index 000000000000..fa27f1c87cff --- /dev/null +++ b/ext/soap/tests/gh23686/import-authorization-basic-lowercase.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-23686: Regression test for `Authorization: Basic` but lowercase (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "authorization: Basic foo bar"], +]); + +check_headers_for_import(LOGS_PATH, $context); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'authorization' => 'Basic foo bar', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'authorization' => 'Basic foo bar', +)" diff --git a/ext/soap/tests/gh23686/import-authorization-basic.phpt b/ext/soap/tests/gh23686/import-authorization-basic.phpt new file mode 100644 index 000000000000..aa04078851fd --- /dev/null +++ b/ext/soap/tests/gh23686/import-authorization-basic.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-23686: Regression test for `Authorization: Basic` (which was stripped) (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "Authorization: Basic foo bar"], +]); + +check_headers_for_import(LOGS_PATH, $context); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', +)" diff --git a/ext/soap/tests/gh23686/import-authorization-bearer.phpt b/ext/soap/tests/gh23686/import-authorization-bearer.phpt new file mode 100644 index 000000000000..8eb87a526531 --- /dev/null +++ b/ext/soap/tests/gh23686/import-authorization-bearer.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-23686: Regression test for `Authorization: Bearer` (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "Authorization: Bearer secret-token"], +]); + +check_headers_for_import(LOGS_PATH, $context); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Authorization' => 'Bearer secret-token', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Authorization' => 'Bearer secret-token', +)" diff --git a/ext/soap/tests/gh23686/import-authorization-digest.phpt b/ext/soap/tests/gh23686/import-authorization-digest.phpt new file mode 100644 index 000000000000..d74058bb4cf3 --- /dev/null +++ b/ext/soap/tests/gh23686/import-authorization-digest.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-23686: Regression test for `Authorization: Digest` (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "Authorization: Digest secret-token"], +]); + +check_headers_for_import(LOGS_PATH, $context); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Authorization' => 'Digest secret-token', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Authorization' => 'Digest secret-token', +)" diff --git a/ext/soap/tests/gh23686/import-cookies.phpt b/ext/soap/tests/gh23686/import-cookies.phpt new file mode 100644 index 000000000000..d7b47daa556b --- /dev/null +++ b/ext/soap/tests/gh23686/import-cookies.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-23686: Regression test for cookie headers (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "Cookie: foo=bar"], +]); + +check_headers_for_import(LOGS_PATH, $context); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Cookie' => 'foo=bar', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Cookie' => 'foo=bar', +)" diff --git a/ext/soap/tests/gh23686/import-proxy-authorization.phpt b/ext/soap/tests/gh23686/import-proxy-authorization.phpt new file mode 100644 index 000000000000..e0b0bb0a4579 --- /dev/null +++ b/ext/soap/tests/gh23686/import-proxy-authorization.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-23686: Regression test for proxy authorization (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "Proxy-Authorization: FooBar"], +]); + +check_headers_for_import(LOGS_PATH, $context); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Proxy-Authorization' => 'FooBar', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Proxy-Authorization' => 'FooBar', +)" diff --git a/ext/soap/tests/gh23686/import-repeated-authorization.phpt b/ext/soap/tests/gh23686/import-repeated-authorization.phpt new file mode 100644 index 000000000000..3b7d5c167269 --- /dev/null +++ b/ext/soap/tests/gh23686/import-repeated-authorization.phpt @@ -0,0 +1,44 @@ +--TEST-- +GH-23686: Regression tests for repeated authorization headers (which doesn't work) (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => [ + "Authorization: Foo", + "aUTHORIZATION: Bar", + ]], +]); + +check_headers_for_import(LOGS_PATH, $context, false); + +?> +--EXPECTF-- +SoapFault: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://localhost:%d/index.php' : failed to load external entity "http://localhost:%d/index.php" + +bool(false) +SoapFault: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://localhost:%d/index.php' : failed to load external entity "http://localhost:%d/index.php" + +bool(false) +Server exited with non-zero status: 1 + +Warning: file_get_contents(): Failed to open stream: No such file or directory in %s on line %d +Server output: diff --git a/ext/soap/tests/gh23686/import-repeated-cookies.phpt b/ext/soap/tests/gh23686/import-repeated-cookies.phpt new file mode 100644 index 000000000000..ebe67c075f36 --- /dev/null +++ b/ext/soap/tests/gh23686/import-repeated-cookies.phpt @@ -0,0 +1,44 @@ +--TEST-- +GH-23686: Regression tests for repeated cookies headers (which doesn't work) (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => [ + "Cookie: Foo", + "cOOKIE: Bar", + ]], +]); + +check_headers_for_import(LOGS_PATH, $context, false); + +?> +--EXPECTF-- +SoapFault: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://localhost:%d/index.php' : failed to load external entity "http://localhost:%d/index.php" + +bool(false) +SoapFault: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://localhost:%d/index.php' : failed to load external entity "http://localhost:%d/index.php" + +bool(false) +Server exited with non-zero status: 1 + +Warning: file_get_contents(): Failed to open stream: No such file or directory in %s on line %d +Server output: diff --git a/ext/soap/tests/gh23686/import-repeated-proxy-auth.phpt b/ext/soap/tests/gh23686/import-repeated-proxy-auth.phpt new file mode 100644 index 000000000000..edeb149e4ca6 --- /dev/null +++ b/ext/soap/tests/gh23686/import-repeated-proxy-auth.phpt @@ -0,0 +1,44 @@ +--TEST-- +GH-23686: Regression tests for repeated proxy authorization headers (which doesn't work) (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => [ + "Proxy-Authorization: Foo", + "pROXY-aUTHORIZATION: Bar", + ]], +]); + +check_headers_for_import(LOGS_PATH, $context, false); + +?> +--EXPECTF-- +SoapFault: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://localhost:%d/index.php' : failed to load external entity "http://localhost:%d/index.php" + +bool(false) +SoapFault: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://localhost:%d/index.php' : failed to load external entity "http://localhost:%d/index.php" + +bool(false) +Server exited with non-zero status: 1 + +Warning: file_get_contents(): Failed to open stream: No such file or directory in %s on line %d +Server output: diff --git a/ext/soap/tests/gh23686/schema-authorization-basic-lowercase.phpt b/ext/soap/tests/gh23686/schema-authorization-basic-lowercase.phpt new file mode 100644 index 000000000000..dedee8b85c41 --- /dev/null +++ b/ext/soap/tests/gh23686/schema-authorization-basic-lowercase.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-23686: Regression test for `Authorization: Basic` but lowercase (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "authorization: Basic foo bar"], +]); + +check_headers_for_schema(LOGS_PATH, $context); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'authorization' => 'Basic foo bar', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'authorization' => 'Basic foo bar', +)" diff --git a/ext/soap/tests/gh23686/schema-authorization-basic.phpt b/ext/soap/tests/gh23686/schema-authorization-basic.phpt new file mode 100644 index 000000000000..22a0ecb311ab --- /dev/null +++ b/ext/soap/tests/gh23686/schema-authorization-basic.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-23686: Regression test for `Authorization: Basic` (which was stripped) (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "Authorization: Basic foo bar"], +]); + +check_headers_for_schema(LOGS_PATH, $context); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', +)" diff --git a/ext/soap/tests/gh23686/schema-authorization-bearer.phpt b/ext/soap/tests/gh23686/schema-authorization-bearer.phpt new file mode 100644 index 000000000000..be786c8f6b16 --- /dev/null +++ b/ext/soap/tests/gh23686/schema-authorization-bearer.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-23686: Regression test for `Authorization: Bearer` (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "Authorization: Bearer secret-token"], +]); + +check_headers_for_schema(LOGS_PATH, $context); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Authorization' => 'Bearer secret-token', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Authorization' => 'Bearer secret-token', +)" diff --git a/ext/soap/tests/gh23686/schema-authorization-digest.phpt b/ext/soap/tests/gh23686/schema-authorization-digest.phpt new file mode 100644 index 000000000000..ad48118d6c05 --- /dev/null +++ b/ext/soap/tests/gh23686/schema-authorization-digest.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-23686: Regression test for `Authorization: Digest` (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "Authorization: Digest secret-token"], +]); + +check_headers_for_schema(LOGS_PATH, $context); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Authorization' => 'Digest secret-token', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Authorization' => 'Digest secret-token', +)" diff --git a/ext/soap/tests/gh23686/schema-cookies.phpt b/ext/soap/tests/gh23686/schema-cookies.phpt new file mode 100644 index 000000000000..ff33d8ace5da --- /dev/null +++ b/ext/soap/tests/gh23686/schema-cookies.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-23686: Regression test for cookie headers (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "Cookie: foo=bar"], +]); + +check_headers_for_schema(LOGS_PATH, $context); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Cookie' => 'foo=bar', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Cookie' => 'foo=bar', +)" diff --git a/ext/soap/tests/gh23686/schema-proxy-authorization.phpt b/ext/soap/tests/gh23686/schema-proxy-authorization.phpt new file mode 100644 index 000000000000..2f06bc9e7358 --- /dev/null +++ b/ext/soap/tests/gh23686/schema-proxy-authorization.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-23686: Regression test for proxy authorization (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "Proxy-Authorization: FooBar"], +]); + +check_headers_for_schema(LOGS_PATH, $context); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Proxy-Authorization' => 'FooBar', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Proxy-Authorization' => 'FooBar', +)" diff --git a/ext/soap/tests/gh23686/schema-repeated-authorization.phpt b/ext/soap/tests/gh23686/schema-repeated-authorization.phpt new file mode 100644 index 000000000000..e99c7c9c7d97 --- /dev/null +++ b/ext/soap/tests/gh23686/schema-repeated-authorization.phpt @@ -0,0 +1,42 @@ +--TEST-- +GH-23686: Regression tests for repeated authorization headers (which doesn't work) (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => [ + "Authorization: Foo", + "aUTHORIZATION: Bar", + ]], +]); + +check_headers_for_schema(LOGS_PATH, $context, false); + +?> +--EXPECTF-- +SoapFault: SOAP-ERROR: Parsing Schema: can't import schema from 'http://localhost:%d/index.php' +bool(false) +SoapFault: SOAP-ERROR: Parsing Schema: can't import schema from 'http://localhost:%d/index.php' +bool(false) +Server exited with non-zero status: 1 + +Warning: file_get_contents(): Failed to open stream: No such file or directory in %s on line %d +Server output: diff --git a/ext/soap/tests/gh23686/schema-repeated-cookies.phpt b/ext/soap/tests/gh23686/schema-repeated-cookies.phpt new file mode 100644 index 000000000000..76cc2b95a6dd --- /dev/null +++ b/ext/soap/tests/gh23686/schema-repeated-cookies.phpt @@ -0,0 +1,42 @@ +--TEST-- +GH-23686: Regression tests for repeated cookies headers (which doesn't work) (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => [ + "Cookie: Foo", + "cOOKIE: Bar", + ]], +]); + +check_headers_for_schema(LOGS_PATH, $context, false); + +?> +--EXPECTF-- +SoapFault: SOAP-ERROR: Parsing Schema: can't import schema from 'http://localhost:%d/index.php' +bool(false) +SoapFault: SOAP-ERROR: Parsing Schema: can't import schema from 'http://localhost:%d/index.php' +bool(false) +Server exited with non-zero status: 1 + +Warning: file_get_contents(): Failed to open stream: No such file or directory in %s on line %d +Server output: diff --git a/ext/soap/tests/gh23686/schema-repeated-proxy-auth.phpt b/ext/soap/tests/gh23686/schema-repeated-proxy-auth.phpt new file mode 100644 index 000000000000..9e6b2e6768b3 --- /dev/null +++ b/ext/soap/tests/gh23686/schema-repeated-proxy-auth.phpt @@ -0,0 +1,42 @@ +--TEST-- +GH-23686: Regression tests for repeated proxy authorization headers (which doesn't work) (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => [ + "Proxy-Authorization: Foo", + "pROXY-aUTHORIZATION: Bar", + ]], +]); + +check_headers_for_schema(LOGS_PATH, $context, false); + +?> +--EXPECTF-- +SoapFault: SOAP-ERROR: Parsing Schema: can't import schema from 'http://localhost:%d/index.php' +bool(false) +SoapFault: SOAP-ERROR: Parsing Schema: can't import schema from 'http://localhost:%d/index.php' +bool(false) +Server exited with non-zero status: 1 + +Warning: file_get_contents(): Failed to open stream: No such file or directory in %s on line %d +Server output: diff --git a/ext/soap/tests/gh23686/schema-respond-with-headers.inc b/ext/soap/tests/gh23686/schema-respond-with-headers.inc new file mode 100644 index 000000000000..a2e97c217792 --- /dev/null +++ b/ext/soap/tests/gh23686/schema-respond-with-headers.inc @@ -0,0 +1,9 @@ + + + +END; \ No newline at end of file diff --git a/ext/soap/tests/gh23686/soap-respond-with-headers.inc b/ext/soap/tests/gh23686/soap-respond-with-headers.inc new file mode 100644 index 000000000000..a85da1071cfd --- /dev/null +++ b/ext/soap/tests/gh23686/soap-respond-with-headers.inc @@ -0,0 +1,16 @@ + + + + + + + + + + +END; \ No newline at end of file diff --git a/ext/soap/tests/gh23686/wsdl-with-import.wsdl b/ext/soap/tests/gh23686/wsdl-with-import.wsdl new file mode 100644 index 000000000000..97a4806fea82 --- /dev/null +++ b/ext/soap/tests/gh23686/wsdl-with-import.wsdl @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/ext/soap/tests/gh23686/wsdl-with-schema.wsdl b/ext/soap/tests/gh23686/wsdl-with-schema.wsdl new file mode 100644 index 000000000000..114d7f6c5d63 --- /dev/null +++ b/ext/soap/tests/gh23686/wsdl-with-schema.wsdl @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sapi/cli/tests/php_cli_server.inc b/sapi/cli/tests/php_cli_server.inc index ec370753573c..6e5449f5eb15 100644 --- a/sapi/cli/tests/php_cli_server.inc +++ b/sapi/cli/tests/php_cli_server.inc @@ -6,6 +6,7 @@ class CliServerInfo { public string $docRoot, public $processHandle, public $outputFile, + public int $port, ) {} } @@ -119,7 +120,7 @@ function php_cli_server_start( define("PHP_CLI_SERVER_PORT", $port); define("PHP_CLI_SERVER_ADDRESS", PHP_CLI_SERVER_HOSTNAME.":".PHP_CLI_SERVER_PORT); - return new CliServerInfo($doc_root, $handle, $output_file); + return new CliServerInfo($doc_root, $handle, $output_file, $port); } function php_cli_server_connect() { From 872542bff8409fbc970063dedb12d7fe401efa0c Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Tue, 15 Sep 2026 20:39:25 -0700 Subject: [PATCH 2/4] `sdl_set_uri_credentials()`: use early returns to improve readability --- ext/soap/php_sdl.c | 60 +++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/ext/soap/php_sdl.c b/ext/soap/php_sdl.c index 3ffa67cfb910..517f8a6afd2f 100644 --- a/ext/soap/php_sdl.c +++ b/ext/soap/php_sdl.c @@ -248,33 +248,39 @@ void sdl_set_uri_credentials(sdlCtx *ctx, char *uri) l2 -= 4; } } - if (l1 != l2 || memcmp(ctx->sdl->source, uri, l1) != 0) { - /* another server. clear authentication credentals */ - php_libxml_switch_context(NULL, &context); - php_libxml_switch_context(&context, NULL); - if (Z_TYPE(context) != IS_UNDEF) { - zval *context_ptr = &context; - ctx->context = php_stream_context_from_zval(context_ptr, 1); - - if (ctx->context && - (header = php_stream_context_get_option(ctx->context, "http", "header")) != NULL && - Z_TYPE_P(header) == IS_STRING) { - /* TODO: should support header as an array, but this code path is untested */ - s = strstr(Z_STRVAL_P(header), "Authorization: Basic"); - if (s && (s == Z_STRVAL_P(header) || *(s-1) == '\n' || *(s-1) == '\r')) { - char *rest = strstr(s, "\r\n"); - if (rest) { - zval new_header; - - rest += 2; - ZVAL_NEW_STR(&new_header, zend_string_alloc(Z_STRLEN_P(header) - (rest - s), 0)); - memcpy(Z_STRVAL(new_header), Z_STRVAL_P(header), s - Z_STRVAL_P(header)); - memcpy(Z_STRVAL(new_header) + (s - Z_STRVAL_P(header)), rest, Z_STRLEN_P(header) - (rest - Z_STRVAL_P(header)) + 1); - ZVAL_COPY(&ctx->old_header, header); - php_stream_context_set_option(ctx->context, "http", "header", &new_header); - zval_ptr_dtor(&new_header); - } - } + if (l1 == l2 && memcmp(ctx->sdl->source, uri, l1) == 0) { + return; + } + /* another server. clear authentication credentals */ + php_libxml_switch_context(NULL, &context); + php_libxml_switch_context(&context, NULL); + if (Z_TYPE(context) == IS_UNDEF) { + return; + } + zval *context_ptr = &context; + ctx->context = php_stream_context_from_zval(context_ptr, 1); + + if (!ctx->context) { + return; + } + + if ((header = php_stream_context_get_option(ctx->context, "http", "header")) != NULL && + Z_TYPE_P(header) == IS_STRING + ) { + /* TODO: should support header as an array, but this code path is untested */ + s = strstr(Z_STRVAL_P(header), "Authorization: Basic"); + if (s && (s == Z_STRVAL_P(header) || *(s-1) == '\n' || *(s-1) == '\r')) { + char *rest = strstr(s, "\r\n"); + if (rest) { + zval new_header; + + rest += 2; + ZVAL_NEW_STR(&new_header, zend_string_alloc(Z_STRLEN_P(header) - (rest - s), 0)); + memcpy(Z_STRVAL(new_header), Z_STRVAL_P(header), s - Z_STRVAL_P(header)); + memcpy(Z_STRVAL(new_header) + (s - Z_STRVAL_P(header)), rest, Z_STRLEN_P(header) - (rest - Z_STRVAL_P(header)) + 1); + ZVAL_COPY(&ctx->old_header, header); + php_stream_context_set_option(ctx->context, "http", "header", &new_header); + zval_ptr_dtor(&new_header); } } } From 33b31f36c3c30175da7e4fdd8877153451d041e0 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Tue, 15 Sep 2026 21:54:41 -0700 Subject: [PATCH 3/4] ext/soap: redact more sensitive headers --- ext/soap/php_sdl.c | 172 ++++++++++++++++-- .../import-authorization-basic-lowercase.phpt | 2 - .../gh23686/import-authorization-bearer.phpt | 2 - .../gh23686/import-authorization-digest.phpt | 2 - ext/soap/tests/gh23686/import-cookies.phpt | 2 - .../gh23686/import-proxy-authorization.phpt | 2 - .../import-repeated-authorization.phpt | 20 +- .../gh23686/import-repeated-cookies.phpt | 20 +- .../gh23686/import-repeated-proxy-auth.phpt | 20 +- .../tests/gh23686/redact-array-headers.phpt | 74 ++++++++ ext/soap/tests/gh23686/redact-not-first.phpt | 67 +++++++ .../schema-authorization-basic-lowercase.phpt | 2 - .../gh23686/schema-authorization-bearer.phpt | 2 - .../gh23686/schema-authorization-digest.phpt | 2 - ext/soap/tests/gh23686/schema-cookies.phpt | 2 - .../gh23686/schema-proxy-authorization.phpt | 2 - .../schema-repeated-authorization.phpt | 18 +- .../gh23686/schema-repeated-cookies.phpt | 18 +- .../gh23686/schema-repeated-proxy-auth.phpt | 18 +- 19 files changed, 351 insertions(+), 96 deletions(-) create mode 100644 ext/soap/tests/gh23686/redact-array-headers.phpt create mode 100644 ext/soap/tests/gh23686/redact-not-first.phpt diff --git a/ext/soap/php_sdl.c b/ext/soap/php_sdl.c index 517f8a6afd2f..b7a05b0faeda 100644 --- a/ext/soap/php_sdl.c +++ b/ext/soap/php_sdl.c @@ -198,6 +198,67 @@ static int is_wsdl_element(xmlNodePtr node) return 1; } +// Which headers need to be redacted +#define REDACT_NONE 0 +#define REDACT_AUTHORIZATION (1 << 0) +#define REDACT_PROXY_AUTH (1 << 1) +#define REDACT_COOKIE (1 << 2) + +uint32_t header_needs_redaction(zend_string *header) { + uint32_t redaction = REDACT_NONE; + /* The various loops are so that if the first use of "authorization" is in + * some other header, we still redact the actual "authorization" header. */ + char *auth_start = strstr(ZSTR_VAL(header), "authorization:"); + while (auth_start && *auth_start) { + if (auth_start == ZSTR_VAL(header) || *(auth_start - 1) == '\n' || *(auth_start - 1) == '\r') { + redaction |= REDACT_AUTHORIZATION; + break; + } + auth_start = strstr(auth_start + 1, "authorization:"); + } + + char *proxy_start = strstr(ZSTR_VAL(header), "proxy-authorization:"); + while (proxy_start && *proxy_start) { + if (proxy_start == ZSTR_VAL(header) || *(proxy_start - 1) == '\n' || *(proxy_start - 1) == '\r') { + redaction |= REDACT_PROXY_AUTH; + break; + } + proxy_start = strstr(proxy_start + 1, "proxy-authorization:"); + } + + char *cookie_start = strstr(ZSTR_VAL(header), "cookie:"); + while (cookie_start && *cookie_start) { + if (cookie_start == ZSTR_VAL(header) || *(cookie_start - 1) == '\n' || *(cookie_start - 1) == '\r') { + redaction |= REDACT_COOKIE; + break; + } + cookie_start = strstr(cookie_start + 1, "cookie:"); + } + return redaction; +} + +bool redact_header(char *header_start, uint32_t *to_redact) { + /* When redacting, we don't remove the REDACT_* flag from to_redact, because + * a header might have been specified multiple times and we want to remove + * all of those uses. */ + if (*to_redact & REDACT_AUTHORIZATION + && strncmp(header_start, "authorization:", strlen("authorization:")) == 0 + ) { + return true; + } + if (*to_redact & REDACT_PROXY_AUTH + && strncmp(header_start, "proxy-authorization:", strlen("proxy-authorization:")) == 0 + ) { + return true; + } + if (*to_redact & REDACT_COOKIE + && strncmp(header_start, "cookie:", strlen("cookie:")) == 0 + ) { + return true; + } + return false; +} + void sdl_set_uri_credentials(sdlCtx *ctx, char *uri) { char *s; @@ -264,26 +325,105 @@ void sdl_set_uri_credentials(sdlCtx *ctx, char *uri) return; } - if ((header = php_stream_context_get_option(ctx->context, "http", "header")) != NULL && - Z_TYPE_P(header) == IS_STRING - ) { - /* TODO: should support header as an array, but this code path is untested */ - s = strstr(Z_STRVAL_P(header), "Authorization: Basic"); - if (s && (s == Z_STRVAL_P(header) || *(s-1) == '\n' || *(s-1) == '\r')) { - char *rest = strstr(s, "\r\n"); - if (rest) { - zval new_header; + header = php_stream_context_get_option(ctx->context, "http", "header"); + if (header == NULL) { + return; + } - rest += 2; - ZVAL_NEW_STR(&new_header, zend_string_alloc(Z_STRLEN_P(header) - (rest - s), 0)); - memcpy(Z_STRVAL(new_header), Z_STRVAL_P(header), s - Z_STRVAL_P(header)); - memcpy(Z_STRVAL(new_header) + (s - Z_STRVAL_P(header)), rest, Z_STRLEN_P(header) - (rest - Z_STRVAL_P(header)) + 1); - ZVAL_COPY(&ctx->old_header, header); - php_stream_context_set_option(ctx->context, "http", "header", &new_header); - zval_ptr_dtor(&new_header); + zend_string *flat_headers; + if (Z_TYPE_P(header) == IS_STRING) { + flat_headers = zend_string_copy(Z_STR_P(header)); + } else if (Z_TYPE_P(header) == IS_ARRAY) { + smart_str buf = {0}; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(header), zval *value) { + if (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value)) { + if (buf.s) { + smart_str_appendl(&buf, "\r\n", 2); + } + smart_str_append(&buf, Z_STR_P(value)); + } + } ZEND_HASH_FOREACH_END(); + flat_headers = smart_str_extract(&buf); + } else { + return; + } + zend_string *lc_headers = zend_string_tolower(flat_headers); + // Fast path: no redaction needed + uint32_t redaction = header_needs_redaction(lc_headers); + if (redaction == REDACT_NONE) { + zend_string_release(lc_headers); + zend_string_release(flat_headers); + return; + } + // At least one of the headers needs to be stripped. To avoid repeated + // copying and case comparisons, we go through the headers one at a time. + // Will be allocated on the first header that should be kept. + zend_string *redacted = NULL; + // Pointer to the current read location + char *read_ptr = ZSTR_VAL(flat_headers); + // Offset from the start of the read location + size_t read_offset = 0; + // Offset from the start of the write location + size_t write_offset = 0; + + while (true) { + if (redact_header(ZSTR_VAL(lc_headers) + read_offset, &redaction)) { + // Current header should be skipped + char *header_end = strstr(read_ptr, "\r\n"); + if (header_end) { + // Skip the current header and the \r\n + size_t header_length = (header_end - read_ptr) + 2; + read_offset += header_length; + read_ptr += header_length; + } else { + // This is the end + break; + } + } else { + // Current header (from `read_offset`) should be kept + if (redacted == NULL) { + // This will over allocate the redacted header string but + // that isn't a big deal, the headers should be fairly short + redacted = zend_string_alloc(ZSTR_LEN(flat_headers), false); + } + char *header_end = strstr(read_ptr, "\r\n"); + if (header_end) { + // Copy the current header and the \r\n + size_t header_length = (header_end - read_ptr) + 2; + memcpy(ZSTR_VAL(redacted) + write_offset, read_ptr, header_length); + // Update offsets + read_offset += header_length; + read_ptr += header_length; + write_offset += header_length; + } else { + // Copy until the end of the headers + size_t header_length = ZSTR_LEN(lc_headers) - read_offset; + memcpy(ZSTR_VAL(redacted) + write_offset, read_ptr, header_length); + write_offset += header_length; + break; } } } + zend_string_release(lc_headers); + zend_string_release(flat_headers); + + ZVAL_COPY(&ctx->old_header, header); + zval new_header; + if (redacted == NULL) { + // All headers were redacted + ZVAL_EMPTY_STRING(&new_header); + } else { + // Only some headers were redacted + // The zval_ptr_dtor call will ensure that the redacted headers + // are freed; + // Make sure that we don't read garbage + ZSTR_VAL(redacted)[write_offset] = '\0'; + ZSTR_LEN(redacted) = write_offset; + + ZVAL_STR(&new_header, redacted); + } + php_stream_context_set_option(ctx->context, "http", "header", &new_header); + zval_ptr_dtor(&new_header); } void sdl_restore_uri_credentials(sdlCtx *ctx) diff --git a/ext/soap/tests/gh23686/import-authorization-basic-lowercase.phpt b/ext/soap/tests/gh23686/import-authorization-basic-lowercase.phpt index fa27f1c87cff..7d0c694dc638 100644 --- a/ext/soap/tests/gh23686/import-authorization-basic-lowercase.phpt +++ b/ext/soap/tests/gh23686/import-authorization-basic-lowercase.phpt @@ -32,10 +32,8 @@ check_headers_for_import(LOGS_PATH, $context); string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'authorization' => 'Basic foo bar', )" string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'authorization' => 'Basic foo bar', )" diff --git a/ext/soap/tests/gh23686/import-authorization-bearer.phpt b/ext/soap/tests/gh23686/import-authorization-bearer.phpt index 8eb87a526531..e8445e8b3264 100644 --- a/ext/soap/tests/gh23686/import-authorization-bearer.phpt +++ b/ext/soap/tests/gh23686/import-authorization-bearer.phpt @@ -32,10 +32,8 @@ check_headers_for_import(LOGS_PATH, $context); string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'Authorization' => 'Bearer secret-token', )" string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'Authorization' => 'Bearer secret-token', )" diff --git a/ext/soap/tests/gh23686/import-authorization-digest.phpt b/ext/soap/tests/gh23686/import-authorization-digest.phpt index d74058bb4cf3..ff3374b0c848 100644 --- a/ext/soap/tests/gh23686/import-authorization-digest.phpt +++ b/ext/soap/tests/gh23686/import-authorization-digest.phpt @@ -32,10 +32,8 @@ check_headers_for_import(LOGS_PATH, $context); string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'Authorization' => 'Digest secret-token', )" string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'Authorization' => 'Digest secret-token', )" diff --git a/ext/soap/tests/gh23686/import-cookies.phpt b/ext/soap/tests/gh23686/import-cookies.phpt index d7b47daa556b..7871ca0874fe 100644 --- a/ext/soap/tests/gh23686/import-cookies.phpt +++ b/ext/soap/tests/gh23686/import-cookies.phpt @@ -32,10 +32,8 @@ check_headers_for_import(LOGS_PATH, $context); string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'Cookie' => 'foo=bar', )" string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'Cookie' => 'foo=bar', )" diff --git a/ext/soap/tests/gh23686/import-proxy-authorization.phpt b/ext/soap/tests/gh23686/import-proxy-authorization.phpt index e0b0bb0a4579..f4356f312840 100644 --- a/ext/soap/tests/gh23686/import-proxy-authorization.phpt +++ b/ext/soap/tests/gh23686/import-proxy-authorization.phpt @@ -32,10 +32,8 @@ check_headers_for_import(LOGS_PATH, $context); string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'Proxy-Authorization' => 'FooBar', )" string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'Proxy-Authorization' => 'FooBar', )" diff --git a/ext/soap/tests/gh23686/import-repeated-authorization.phpt b/ext/soap/tests/gh23686/import-repeated-authorization.phpt index 3b7d5c167269..7ec369b7b92b 100644 --- a/ext/soap/tests/gh23686/import-repeated-authorization.phpt +++ b/ext/soap/tests/gh23686/import-repeated-authorization.phpt @@ -1,5 +1,5 @@ --TEST-- -GH-23686: Regression tests for repeated authorization headers (which doesn't work) (``) +GH-23686: Regression tests for repeated authorization headers (which previously didn't work) (``) --EXTENSIONS-- soap --INI-- @@ -32,13 +32,11 @@ check_headers_for_import(LOGS_PATH, $context, false); ?> --EXPECTF-- -SoapFault: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://localhost:%d/index.php' : failed to load external entity "http://localhost:%d/index.php" - -bool(false) -SoapFault: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://localhost:%d/index.php' : failed to load external entity "http://localhost:%d/index.php" - -bool(false) -Server exited with non-zero status: 1 - -Warning: file_get_contents(): Failed to open stream: No such file or directory in %s on line %d -Server output: +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', +)" diff --git a/ext/soap/tests/gh23686/import-repeated-cookies.phpt b/ext/soap/tests/gh23686/import-repeated-cookies.phpt index ebe67c075f36..b6c4f2e1d1db 100644 --- a/ext/soap/tests/gh23686/import-repeated-cookies.phpt +++ b/ext/soap/tests/gh23686/import-repeated-cookies.phpt @@ -1,5 +1,5 @@ --TEST-- -GH-23686: Regression tests for repeated cookies headers (which doesn't work) (``) +GH-23686: Regression tests for repeated cookies headers (which previously didn't work) (``) --EXTENSIONS-- soap --INI-- @@ -32,13 +32,11 @@ check_headers_for_import(LOGS_PATH, $context, false); ?> --EXPECTF-- -SoapFault: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://localhost:%d/index.php' : failed to load external entity "http://localhost:%d/index.php" - -bool(false) -SoapFault: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://localhost:%d/index.php' : failed to load external entity "http://localhost:%d/index.php" - -bool(false) -Server exited with non-zero status: 1 - -Warning: file_get_contents(): Failed to open stream: No such file or directory in %s on line %d -Server output: +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', +)" diff --git a/ext/soap/tests/gh23686/import-repeated-proxy-auth.phpt b/ext/soap/tests/gh23686/import-repeated-proxy-auth.phpt index edeb149e4ca6..4628fcb7fe0b 100644 --- a/ext/soap/tests/gh23686/import-repeated-proxy-auth.phpt +++ b/ext/soap/tests/gh23686/import-repeated-proxy-auth.phpt @@ -1,5 +1,5 @@ --TEST-- -GH-23686: Regression tests for repeated proxy authorization headers (which doesn't work) (``) +GH-23686: Regression tests for repeated proxy authorization headers (which previously didn't work) (``) --EXTENSIONS-- soap --INI-- @@ -32,13 +32,11 @@ check_headers_for_import(LOGS_PATH, $context, false); ?> --EXPECTF-- -SoapFault: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://localhost:%d/index.php' : failed to load external entity "http://localhost:%d/index.php" - -bool(false) -SoapFault: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://localhost:%d/index.php' : failed to load external entity "http://localhost:%d/index.php" - -bool(false) -Server exited with non-zero status: 1 - -Warning: file_get_contents(): Failed to open stream: No such file or directory in %s on line %d -Server output: +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', +)" diff --git a/ext/soap/tests/gh23686/redact-array-headers.phpt b/ext/soap/tests/gh23686/redact-array-headers.phpt new file mode 100644 index 000000000000..c65f77daf77b --- /dev/null +++ b/ext/soap/tests/gh23686/redact-array-headers.phpt @@ -0,0 +1,74 @@ +--TEST-- +GH-23686: Verify that a header is redacted when it reaches sdl_set_uri_credentials() as an array +--DESCRIPTION-- +Most of the time soap will add its own headers and in the process convert user headers +from an array to a string, check for the case when that does not happen +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + [ + // having a protocol here means that SOAP won't add it itself + 'protocol_version' => 1.1, + 'header' => [ + "X-Authorization: This should be kept", + "Authorization: This should be removed", + "X-Proxy-Authorization: This should also be kept", + "Proxy-Authorization: This should also be removed", + "X-Cookie: Last one to keep", + "Cookie: Last one to remove", + ] + ], +]); + +check_headers_for_import(LOGS_PATH, $context); +check_headers_for_schema(LOGS_PATH, $context); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'X-Authorization' => 'This should be kept', + 'X-Proxy-Authorization' => 'This should also be kept', + 'X-Cookie' => 'Last one to keep', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'X-Authorization' => 'This should be kept', + 'X-Proxy-Authorization' => 'This should also be kept', + 'X-Cookie' => 'Last one to keep', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'X-Authorization' => 'This should be kept', + 'X-Proxy-Authorization' => 'This should also be kept', + 'X-Cookie' => 'Last one to keep', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'X-Authorization' => 'This should be kept', + 'X-Proxy-Authorization' => 'This should also be kept', + 'X-Cookie' => 'Last one to keep', +)" diff --git a/ext/soap/tests/gh23686/redact-not-first.phpt b/ext/soap/tests/gh23686/redact-not-first.phpt new file mode 100644 index 000000000000..f85b80f47b56 --- /dev/null +++ b/ext/soap/tests/gh23686/redact-not-first.phpt @@ -0,0 +1,67 @@ +--TEST-- +GH-23686: Verify that a header is redacted when it isn't the first use of the name +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => [ + "X-Authorization: This should be kept", + "Authorization: This should be removed", + "X-Proxy-Authorization: This should also be kept", + "Proxy-Authorization: This should also be removed", + "X-Cookie: Last one to keep", + "Cookie: Last one to remove", + ]], +]); + +check_headers_for_import(LOGS_PATH, $context); +check_headers_for_schema(LOGS_PATH, $context); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'X-Authorization' => 'This should be kept', + 'X-Proxy-Authorization' => 'This should also be kept', + 'X-Cookie' => 'Last one to keep', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'X-Authorization' => 'This should be kept', + 'X-Proxy-Authorization' => 'This should also be kept', + 'X-Cookie' => 'Last one to keep', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'X-Authorization' => 'This should be kept', + 'X-Proxy-Authorization' => 'This should also be kept', + 'X-Cookie' => 'Last one to keep', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'X-Authorization' => 'This should be kept', + 'X-Proxy-Authorization' => 'This should also be kept', + 'X-Cookie' => 'Last one to keep', +)" diff --git a/ext/soap/tests/gh23686/schema-authorization-basic-lowercase.phpt b/ext/soap/tests/gh23686/schema-authorization-basic-lowercase.phpt index dedee8b85c41..8a55c870d7af 100644 --- a/ext/soap/tests/gh23686/schema-authorization-basic-lowercase.phpt +++ b/ext/soap/tests/gh23686/schema-authorization-basic-lowercase.phpt @@ -32,10 +32,8 @@ check_headers_for_schema(LOGS_PATH, $context); string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'authorization' => 'Basic foo bar', )" string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'authorization' => 'Basic foo bar', )" diff --git a/ext/soap/tests/gh23686/schema-authorization-bearer.phpt b/ext/soap/tests/gh23686/schema-authorization-bearer.phpt index be786c8f6b16..8b575c74edd8 100644 --- a/ext/soap/tests/gh23686/schema-authorization-bearer.phpt +++ b/ext/soap/tests/gh23686/schema-authorization-bearer.phpt @@ -32,10 +32,8 @@ check_headers_for_schema(LOGS_PATH, $context); string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'Authorization' => 'Bearer secret-token', )" string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'Authorization' => 'Bearer secret-token', )" diff --git a/ext/soap/tests/gh23686/schema-authorization-digest.phpt b/ext/soap/tests/gh23686/schema-authorization-digest.phpt index ad48118d6c05..a218e694d77c 100644 --- a/ext/soap/tests/gh23686/schema-authorization-digest.phpt +++ b/ext/soap/tests/gh23686/schema-authorization-digest.phpt @@ -32,10 +32,8 @@ check_headers_for_schema(LOGS_PATH, $context); string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'Authorization' => 'Digest secret-token', )" string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'Authorization' => 'Digest secret-token', )" diff --git a/ext/soap/tests/gh23686/schema-cookies.phpt b/ext/soap/tests/gh23686/schema-cookies.phpt index ff33d8ace5da..57c5984eee8a 100644 --- a/ext/soap/tests/gh23686/schema-cookies.phpt +++ b/ext/soap/tests/gh23686/schema-cookies.phpt @@ -32,10 +32,8 @@ check_headers_for_schema(LOGS_PATH, $context); string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'Cookie' => 'foo=bar', )" string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'Cookie' => 'foo=bar', )" diff --git a/ext/soap/tests/gh23686/schema-proxy-authorization.phpt b/ext/soap/tests/gh23686/schema-proxy-authorization.phpt index 2f06bc9e7358..51920558c9b8 100644 --- a/ext/soap/tests/gh23686/schema-proxy-authorization.phpt +++ b/ext/soap/tests/gh23686/schema-proxy-authorization.phpt @@ -32,10 +32,8 @@ check_headers_for_schema(LOGS_PATH, $context); string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'Proxy-Authorization' => 'FooBar', )" string(%d) "array ( 'Host' => 'localhost:%d', 'Connection' => 'close', - 'Proxy-Authorization' => 'FooBar', )" diff --git a/ext/soap/tests/gh23686/schema-repeated-authorization.phpt b/ext/soap/tests/gh23686/schema-repeated-authorization.phpt index e99c7c9c7d97..066e1541b0b9 100644 --- a/ext/soap/tests/gh23686/schema-repeated-authorization.phpt +++ b/ext/soap/tests/gh23686/schema-repeated-authorization.phpt @@ -1,5 +1,5 @@ --TEST-- -GH-23686: Regression tests for repeated authorization headers (which doesn't work) (``) +GH-23686: Regression tests for repeated authorization headers (which previously didn't work) (``) --EXTENSIONS-- soap --INI-- @@ -32,11 +32,11 @@ check_headers_for_schema(LOGS_PATH, $context, false); ?> --EXPECTF-- -SoapFault: SOAP-ERROR: Parsing Schema: can't import schema from 'http://localhost:%d/index.php' -bool(false) -SoapFault: SOAP-ERROR: Parsing Schema: can't import schema from 'http://localhost:%d/index.php' -bool(false) -Server exited with non-zero status: 1 - -Warning: file_get_contents(): Failed to open stream: No such file or directory in %s on line %d -Server output: +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', +)" diff --git a/ext/soap/tests/gh23686/schema-repeated-cookies.phpt b/ext/soap/tests/gh23686/schema-repeated-cookies.phpt index 76cc2b95a6dd..5eaa8ef9465e 100644 --- a/ext/soap/tests/gh23686/schema-repeated-cookies.phpt +++ b/ext/soap/tests/gh23686/schema-repeated-cookies.phpt @@ -1,5 +1,5 @@ --TEST-- -GH-23686: Regression tests for repeated cookies headers (which doesn't work) (``) +GH-23686: Regression tests for repeated cookies headers (which previously didn't work) (``) --EXTENSIONS-- soap --INI-- @@ -32,11 +32,11 @@ check_headers_for_schema(LOGS_PATH, $context, false); ?> --EXPECTF-- -SoapFault: SOAP-ERROR: Parsing Schema: can't import schema from 'http://localhost:%d/index.php' -bool(false) -SoapFault: SOAP-ERROR: Parsing Schema: can't import schema from 'http://localhost:%d/index.php' -bool(false) -Server exited with non-zero status: 1 - -Warning: file_get_contents(): Failed to open stream: No such file or directory in %s on line %d -Server output: +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', +)" diff --git a/ext/soap/tests/gh23686/schema-repeated-proxy-auth.phpt b/ext/soap/tests/gh23686/schema-repeated-proxy-auth.phpt index 9e6b2e6768b3..e23759b03a9b 100644 --- a/ext/soap/tests/gh23686/schema-repeated-proxy-auth.phpt +++ b/ext/soap/tests/gh23686/schema-repeated-proxy-auth.phpt @@ -1,5 +1,5 @@ --TEST-- -GH-23686: Regression tests for repeated proxy authorization headers (which doesn't work) (``) +GH-23686: Regression tests for repeated proxy authorization headers (which previously didn't work) (``) --EXTENSIONS-- soap --INI-- @@ -32,11 +32,11 @@ check_headers_for_schema(LOGS_PATH, $context, false); ?> --EXPECTF-- -SoapFault: SOAP-ERROR: Parsing Schema: can't import schema from 'http://localhost:%d/index.php' -bool(false) -SoapFault: SOAP-ERROR: Parsing Schema: can't import schema from 'http://localhost:%d/index.php' -bool(false) -Server exited with non-zero status: 1 - -Warning: file_get_contents(): Failed to open stream: No such file or directory in %s on line %d -Server output: +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', +)" From b96d6d1c6bf78a4ec4a6ff658c83cba7addb2de8 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Tue, 15 Sep 2026 22:25:21 -0700 Subject: [PATCH 4/4] SoapClient: add new `keep_headers` option --- ext/soap/php_sdl.c | 14 +++++++ ext/soap/php_sdl.h | 1 + ext/soap/php_soap.h | 6 +++ ext/soap/soap.c | 21 ++++++++++ ext/soap/soap.stub.php | 1 + ext/soap/soap_arginfo.h | 8 +++- ext/soap/tests/bugs/gh21421.phpt | 4 +- ext/soap/tests/gh23686/check_headers.inc | 16 +++++++- .../gh23686/import-keep-authorization.phpt | 41 +++++++++++++++++++ .../tests/gh23686/import-keep-cookies.phpt | 41 +++++++++++++++++++ .../tests/gh23686/import-keep-proxy-auth.phpt | 41 +++++++++++++++++++ .../import-repeated-authorization.phpt | 2 +- .../gh23686/import-repeated-cookies.phpt | 2 +- .../gh23686/import-repeated-proxy-auth.phpt | 2 +- .../gh23686/schema-keep-authorization.phpt | 41 +++++++++++++++++++ .../tests/gh23686/schema-keep-cookies.phpt | 41 +++++++++++++++++++ .../tests/gh23686/schema-keep-proxy-auth.phpt | 41 +++++++++++++++++++ .../schema-repeated-authorization.phpt | 2 +- .../gh23686/schema-repeated-cookies.phpt | 2 +- .../gh23686/schema-repeated-proxy-auth.phpt | 2 +- 20 files changed, 319 insertions(+), 10 deletions(-) create mode 100644 ext/soap/tests/gh23686/import-keep-authorization.phpt create mode 100644 ext/soap/tests/gh23686/import-keep-cookies.phpt create mode 100644 ext/soap/tests/gh23686/import-keep-proxy-auth.phpt create mode 100644 ext/soap/tests/gh23686/schema-keep-authorization.phpt create mode 100644 ext/soap/tests/gh23686/schema-keep-cookies.phpt create mode 100644 ext/soap/tests/gh23686/schema-keep-proxy-auth.phpt diff --git a/ext/soap/php_sdl.c b/ext/soap/php_sdl.c index b7a05b0faeda..92bda99981e6 100644 --- a/ext/soap/php_sdl.c +++ b/ext/soap/php_sdl.c @@ -350,6 +350,15 @@ void sdl_set_uri_credentials(sdlCtx *ctx, char *uri) zend_string *lc_headers = zend_string_tolower(flat_headers); // Fast path: no redaction needed uint32_t redaction = header_needs_redaction(lc_headers); + if (ctx->headers_to_keep & WSDL_HEADER_KEEP_AUTHORIZATION) { + redaction &= ~REDACT_AUTHORIZATION; + } + if (ctx->headers_to_keep & WSDL_HEADER_KEEP_PROXY_AUTHORIZATION) { + redaction &= ~REDACT_PROXY_AUTH; + } + if (ctx->headers_to_keep & WSDL_HEADER_KEEP_COOKIES) { + redaction &= ~REDACT_COOKIE; + } if (redaction == REDACT_NONE) { zend_string_release(lc_headers); zend_string_release(flat_headers); @@ -853,6 +862,11 @@ static sdlPtr load_wsdl(zval *this_ptr, char *struri) zend_hash_init(&ctx.portTypes, 0, NULL, NULL, 0); zend_hash_init(&ctx.services, 0, NULL, NULL, 0); + ctx.headers_to_keep = 0; + if (instanceof_function(Z_OBJCE_P(this_ptr), soap_class_entry)) { + ctx.headers_to_keep = Z_LVAL_P(Z_CLIENT_KEEP_HEADERS_P(this_ptr)); + } + zend_try { load_wsdl_ex(this_ptr, struri, &ctx, false); schema_pass2(&ctx); diff --git a/ext/soap/php_sdl.h b/ext/soap/php_sdl.h index 7249f094f165..b076285ebb60 100644 --- a/ext/soap/php_sdl.h +++ b/ext/soap/php_sdl.h @@ -74,6 +74,7 @@ typedef struct sdlCtx { HashTable fixupInProgress; php_stream_context *context; zval old_header; + zend_long headers_to_keep; /* Bitmask of WSDL_HEADER_KEEP_* from php_soap.h */ } sdlCtx; struct _sdlBinding { diff --git a/ext/soap/php_soap.h b/ext/soap/php_soap.h index d3c273615eb6..c067b633f488 100644 --- a/ext/soap/php_soap.h +++ b/ext/soap/php_soap.h @@ -151,6 +151,11 @@ struct _soapService { #define SOAP_MAX_XML_DEPTH 2048 #define SOAP_MAX_DECODE_DEPTH (SOAP_MAX_XML_DEPTH * 2) +/* Some headers are redacted on requests to other hosts unless requested otherwise */ +#define WSDL_HEADER_KEEP_AUTHORIZATION (1 << 0) +#define WSDL_HEADER_KEEP_PROXY_AUTHORIZATION (1 << 1) +#define WSDL_HEADER_KEEP_COOKIES (1 << 2) + ZEND_BEGIN_MODULE_GLOBALS(soap) HashTable *typemap; int cur_uniq_ns; @@ -255,6 +260,7 @@ static zend_always_inline zval *php_soap_deref(zval *zv) { #define Z_CLIENT_LAST_RESPONSE_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 32) #define Z_CLIENT_LAST_REQUEST_HEADERS_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 33) #define Z_CLIENT_LAST_RESPONSE_HEADERS_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 34) +#define Z_CLIENT_KEEP_HEADERS_P(zv) OBJ_PROP_NUM(Z_OBJ_P(zv), 35) typedef struct soap_url_object { php_uri *uri; diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 97a653955825..2dc6802310b0 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -2106,6 +2106,7 @@ PHP_METHOD(SoapClient, __construct) sdlPtr sdl = NULL; HashTable *typemap_ht = NULL; zval *this_ptr = ZEND_THIS; + zend_long keep_headers = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!|a", &wsdl, &options) == FAILURE) { RETURN_THROWS(); @@ -2303,6 +2304,26 @@ PHP_METHOD(SoapClient, __construct) "The \"ssl_method\" option is deprecated. " "Use \"ssl\" stream context options instead"); } + + if ((tmp = zend_hash_str_find(ht, "keep_headers", sizeof("keep_headers")-1)) != NULL + && Z_TYPE_P(tmp) == IS_ARRAY + ) { + ZEND_HASH_FOREACH_VAL(Z_ARR_P(tmp), zval *to_keep) { + if (Z_TYPE_P(to_keep) != IS_STRING) { + continue; + } + zend_string *header_name = Z_STR_P(to_keep); + if (zend_string_equals_literal_ci(header_name, "authorization")) { + keep_headers |= WSDL_HEADER_KEEP_AUTHORIZATION; + } else if (zend_string_equals_literal_ci(header_name, "proxy-authorization")) { + keep_headers |= WSDL_HEADER_KEEP_PROXY_AUTHORIZATION; + } else if (zend_string_equals_literal_ci(header_name, "cookie")) { + keep_headers |= WSDL_HEADER_KEEP_COOKIES; + } + } ZEND_HASH_FOREACH_END(); + ZVAL_LONG(Z_CLIENT_KEEP_HEADERS_P(this_ptr), keep_headers); + + } } else if (!wsdl) { php_error_docref(NULL, E_ERROR, "'location' and 'uri' options are required in nonWSDL mode"); } diff --git a/ext/soap/soap.stub.php b/ext/soap/soap.stub.php index fdd4a46e109f..d614fe8d963d 100644 --- a/ext/soap/soap.stub.php +++ b/ext/soap/soap.stub.php @@ -572,6 +572,7 @@ class SoapClient private ?string $__last_response = null; private ?string $__last_request_headers = null; private ?string $__last_response_headers = null; + private int $_keep_headers = 0; public function __construct(?string $wsdl, array $options = []) {} diff --git a/ext/soap/soap_arginfo.h b/ext/soap/soap_arginfo.h index f6b146d779d6..5757cea7e361 100644 --- a/ext/soap/soap_arginfo.h +++ b/ext/soap/soap_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit soap.stub.php instead. - * Stub hash: 14c74a5d6f547837f536920d5abb741e2b6e4373 */ + * Stub hash: c3509a1552db7edabc48d89eea67ff2e1e7d9115 */ #include "zend_attributes.h" #include "zend_constants.h" @@ -748,5 +748,11 @@ static zend_class_entry *register_class_SoapClient(void) zend_declare_typed_property(class_entry, property___last_response_headers_name, &property___last_response_headers_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); zend_string_release_ex(property___last_response_headers_name, true); + zval property__keep_headers_default_value; + ZVAL_LONG(&property__keep_headers_default_value, 0); + zend_string *property__keep_headers_name = zend_string_init("_keep_headers", sizeof("_keep_headers") - 1, true); + zend_declare_typed_property(class_entry, property__keep_headers_name, &property__keep_headers_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release_ex(property__keep_headers_name, true); + return class_entry; } diff --git a/ext/soap/tests/bugs/gh21421.phpt b/ext/soap/tests/bugs/gh21421.phpt index 1b8bb3be716f..e20b79aed56b 100644 --- a/ext/soap/tests/bugs/gh21421.phpt +++ b/ext/soap/tests/bugs/gh21421.phpt @@ -15,7 +15,7 @@ $client = new SoapClient(NULL, $options); var_dump($client); ?> --EXPECTF-- -object(SoapClient)#%d (35) { +object(SoapClient)#%d (36) { ["uri":"SoapClient":private]=> string(26) "http://schemas.nothing.com" ["style":"SoapClient":private]=> @@ -87,4 +87,6 @@ object(SoapClient)#%d (35) { NULL ["__last_response_headers":"SoapClient":private]=> NULL + ["_keep_headers":"SoapClient":private]=> + int(0) } diff --git a/ext/soap/tests/gh23686/check_headers.inc b/ext/soap/tests/gh23686/check_headers.inc index bc2694d8daff..31ae307ce1df 100644 --- a/ext/soap/tests/gh23686/check_headers.inc +++ b/ext/soap/tests/gh23686/check_headers.inc @@ -20,7 +20,12 @@ function start_server(string $code, ?string $entry_point) { * When an switches to a different server, check the headers that the * second server recieves */ -function check_headers_for_import(string $logs_path, $context, bool $verbose_failure = true) { +function check_headers_for_import( + string $logs_path, + $context, + array $options = [], + bool $verbose_failure = true, +) { $code = file_get_contents(__DIR__ . "/soap-respond-with-headers.inc"); $code = substr($code, strpos($code, " $version, 'stream_context' => $context, + ...$options, ]); } catch (SoapFault $e) { if ($verbose_failure) { @@ -61,7 +67,12 @@ function check_headers_for_import(string $logs_path, $context, bool $verbose_fai * When an leads to a on a different server, check the * headers that the *third* server recieves */ -function check_headers_for_schema(string $logs_path, $context, bool $verbose_failure = true) { +function check_headers_for_schema( + string $logs_path, + $context, + array $options = [], + bool $verbose_failure = true +) { $code = file_get_contents(__DIR__ . "/schema-respond-with-headers.inc"); $code = substr($code, strpos($code, " $version, 'stream_context' => $context, + ...$options, ]); } catch (SoapFault $e) { if ($verbose_failure) { diff --git a/ext/soap/tests/gh23686/import-keep-authorization.phpt b/ext/soap/tests/gh23686/import-keep-authorization.phpt new file mode 100644 index 000000000000..cc545e812931 --- /dev/null +++ b/ext/soap/tests/gh23686/import-keep-authorization.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-23686: `Authorization` header kept but others removed (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "Authorization: Bearer secret-token\r\nProxy-Authorization: FooBar\r\nCookie: foo=bar"], +]); + +check_headers_for_import(LOGS_PATH, $context, ["keep_headers" => ["authorization"]]); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Authorization' => 'Bearer secret-token', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Authorization' => 'Bearer secret-token', +)" diff --git a/ext/soap/tests/gh23686/import-keep-cookies.phpt b/ext/soap/tests/gh23686/import-keep-cookies.phpt new file mode 100644 index 000000000000..0732e3585bd6 --- /dev/null +++ b/ext/soap/tests/gh23686/import-keep-cookies.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-23686: `Cookie` header kept but others removed (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "Authorization: Bearer secret-token\r\nProxy-Authorization: FooBar\r\nCookie: foo=bar"], +]); + +check_headers_for_import(LOGS_PATH, $context, ["keep_headers" => ["cookie"]]); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Cookie' => 'foo=bar', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Cookie' => 'foo=bar', +)" diff --git a/ext/soap/tests/gh23686/import-keep-proxy-auth.phpt b/ext/soap/tests/gh23686/import-keep-proxy-auth.phpt new file mode 100644 index 000000000000..5dcf75f3502a --- /dev/null +++ b/ext/soap/tests/gh23686/import-keep-proxy-auth.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-23686: `Proxy-Authorization` header kept but others removed (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "Authorization: Bearer secret-token\r\nProxy-Authorization: FooBar\r\nCookie: foo=bar"], +]); + +check_headers_for_import(LOGS_PATH, $context, ["keep_headers" => ["proxy-authorization"]]); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Proxy-Authorization' => 'FooBar', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Proxy-Authorization' => 'FooBar', +)" diff --git a/ext/soap/tests/gh23686/import-repeated-authorization.phpt b/ext/soap/tests/gh23686/import-repeated-authorization.phpt index 7ec369b7b92b..2263e538ba96 100644 --- a/ext/soap/tests/gh23686/import-repeated-authorization.phpt +++ b/ext/soap/tests/gh23686/import-repeated-authorization.phpt @@ -28,7 +28,7 @@ $context = stream_context_create([ ]], ]); -check_headers_for_import(LOGS_PATH, $context, false); +check_headers_for_import(LOGS_PATH, $context, [], false); ?> --EXPECTF-- diff --git a/ext/soap/tests/gh23686/import-repeated-cookies.phpt b/ext/soap/tests/gh23686/import-repeated-cookies.phpt index b6c4f2e1d1db..4a7734547f4c 100644 --- a/ext/soap/tests/gh23686/import-repeated-cookies.phpt +++ b/ext/soap/tests/gh23686/import-repeated-cookies.phpt @@ -28,7 +28,7 @@ $context = stream_context_create([ ]], ]); -check_headers_for_import(LOGS_PATH, $context, false); +check_headers_for_import(LOGS_PATH, $context, [], false); ?> --EXPECTF-- diff --git a/ext/soap/tests/gh23686/import-repeated-proxy-auth.phpt b/ext/soap/tests/gh23686/import-repeated-proxy-auth.phpt index 4628fcb7fe0b..9dca3e1a0bbb 100644 --- a/ext/soap/tests/gh23686/import-repeated-proxy-auth.phpt +++ b/ext/soap/tests/gh23686/import-repeated-proxy-auth.phpt @@ -28,7 +28,7 @@ $context = stream_context_create([ ]], ]); -check_headers_for_import(LOGS_PATH, $context, false); +check_headers_for_import(LOGS_PATH, $context, [], false); ?> --EXPECTF-- diff --git a/ext/soap/tests/gh23686/schema-keep-authorization.phpt b/ext/soap/tests/gh23686/schema-keep-authorization.phpt new file mode 100644 index 000000000000..676e0f818849 --- /dev/null +++ b/ext/soap/tests/gh23686/schema-keep-authorization.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-23686: `Authorization` header kept but others removed (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "Authorization: Bearer secret-token\r\nProxy-Authorization: FooBar\r\nCookie: foo=bar"], +]); + +check_headers_for_schema(LOGS_PATH, $context, ["keep_headers" => ["authorization"]]); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Authorization' => 'Bearer secret-token', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Authorization' => 'Bearer secret-token', +)" diff --git a/ext/soap/tests/gh23686/schema-keep-cookies.phpt b/ext/soap/tests/gh23686/schema-keep-cookies.phpt new file mode 100644 index 000000000000..6ba48e3fc24f --- /dev/null +++ b/ext/soap/tests/gh23686/schema-keep-cookies.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-23686: `Cookie` header kept but others removed (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "Authorization: Bearer secret-token\r\nProxy-Authorization: FooBar\r\nCookie: foo=bar"], +]); + +check_headers_for_schema(LOGS_PATH, $context, ["keep_headers" => ["cookie"]]); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Cookie' => 'foo=bar', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Cookie' => 'foo=bar', +)" diff --git a/ext/soap/tests/gh23686/schema-keep-proxy-auth.phpt b/ext/soap/tests/gh23686/schema-keep-proxy-auth.phpt new file mode 100644 index 000000000000..ae45bb74b217 --- /dev/null +++ b/ext/soap/tests/gh23686/schema-keep-proxy-auth.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-23686: `Proxy-Authorization` header kept but others removed (``) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--CLEAN-- + +--SKIPIF-- + +--FILE-- + ['header' => "Authorization: Bearer secret-token\r\nProxy-Authorization: FooBar\r\nCookie: foo=bar"], +]); + +check_headers_for_schema(LOGS_PATH, $context, ["keep_headers" => ["proxy-authorization"]]); + +?> +--EXPECTF-- +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Proxy-Authorization' => 'FooBar', +)" +string(%d) "array ( + 'Host' => 'localhost:%d', + 'Connection' => 'close', + 'Proxy-Authorization' => 'FooBar', +)" diff --git a/ext/soap/tests/gh23686/schema-repeated-authorization.phpt b/ext/soap/tests/gh23686/schema-repeated-authorization.phpt index 066e1541b0b9..d23d2592d8ca 100644 --- a/ext/soap/tests/gh23686/schema-repeated-authorization.phpt +++ b/ext/soap/tests/gh23686/schema-repeated-authorization.phpt @@ -28,7 +28,7 @@ $context = stream_context_create([ ]], ]); -check_headers_for_schema(LOGS_PATH, $context, false); +check_headers_for_schema(LOGS_PATH, $context, [], false); ?> --EXPECTF-- diff --git a/ext/soap/tests/gh23686/schema-repeated-cookies.phpt b/ext/soap/tests/gh23686/schema-repeated-cookies.phpt index 5eaa8ef9465e..dfc01e966a62 100644 --- a/ext/soap/tests/gh23686/schema-repeated-cookies.phpt +++ b/ext/soap/tests/gh23686/schema-repeated-cookies.phpt @@ -28,7 +28,7 @@ $context = stream_context_create([ ]], ]); -check_headers_for_schema(LOGS_PATH, $context, false); +check_headers_for_schema(LOGS_PATH, $context, [], false); ?> --EXPECTF-- diff --git a/ext/soap/tests/gh23686/schema-repeated-proxy-auth.phpt b/ext/soap/tests/gh23686/schema-repeated-proxy-auth.phpt index e23759b03a9b..ea83b7ad9e85 100644 --- a/ext/soap/tests/gh23686/schema-repeated-proxy-auth.phpt +++ b/ext/soap/tests/gh23686/schema-repeated-proxy-auth.phpt @@ -28,7 +28,7 @@ $context = stream_context_create([ ]], ]); -check_headers_for_schema(LOGS_PATH, $context, false); +check_headers_for_schema(LOGS_PATH, $context, [], false); ?> --EXPECTF--