Skip to content

Request #64137: XSLTProcessor::setParameter() should allow both quotes to be used #12331

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions ext/xsl/tests/bug48221.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ $proc->importStylesheet($xsl);
$proc->setParameter('', '', '"\'');
$proc->transformToXml($dom);
?>
--EXPECTF--
Warning: XSLTProcessor::transformToXml(): Cannot create XPath expression (string contains both quote and double-quotes) in %s on line %d
Done
--EXPECT--
Done
--CREDITS--
Christian Weiske, [email protected]
PHP Testfest Berlin 2009-05-09
35 changes: 35 additions & 0 deletions ext/xsl/tests/bug64137.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
--TEST--
Request #64137 (XSLTProcessor::setParameter() should allow both quotes to be used)
--EXTENSIONS--
xsl
--FILE--
<?php

function test(string $input) {
$xml = new DOMDocument;
$xml->loadXML('<X/>');

$xsl = new DOMDocument;
$xsl->loadXML('<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:output method="text"/><xsl:param name="foo"/><xsl:template match="/"><xsl:value-of select="$foo"/></xsl:template></xsl:stylesheet>');

$xslt = new XSLTProcessor;
$xslt->importStylesheet($xsl);
$xslt->setParameter('', 'foo', $input);

echo $xslt->transformToXml($xml), "\n";
}

test("");
test("a'");
test("a\"");
test("fo'o\"ba'r\"ba'z");
test("\"\"\"fo'o\"ba'r\"ba'z\"\"\"");
test("'''\"\"\"fo'o\"ba'r\"ba'z\"\"\"'''");

?>
--EXPECT--
a'
a"
fo'o"ba'r"ba'z
"""fo'o"ba'r"ba'z"""
'''"""fo'o"ba'r"ba'z"""'''
102 changes: 102 additions & 0 deletions ext/xsl/tests/setParameter_exceptions_test.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
--TEST--
setParameter exceptions test
--EXTENSIONS--
xsl
--FILE--
<?php

function test(array $options) {
$xml = new DOMDocument;
$xml->loadXML('<X/>');

$xsl = new DOMDocument;
$xsl->loadXML('<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:output method="text"/><xsl:param name="foo"/><xsl:template match="/"><xsl:value-of select="$foo"/></xsl:template></xsl:stylesheet>');

$xslt = new XSLTProcessor;
$xslt->importStylesheet($xsl);
$xslt->setParameter('', $options);

echo $xslt->transformToXml($xml), "\n";
}

echo "--- Invalid key ---\n";

try {
test([
12345 => "foo"
]);
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}

echo "--- Valid key and value, but special cases ---\n";

test([
"foo" => null,
]);

test([
"foo" => true,
]);

echo "--- Exception from Stringable should abort execution ---\n";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
echo "--- Exception from Stringable should abort execution ---\n";
echo "--- Exception from __toString should abort execution ---\n";

Might be clearer, as I was slightly confused just reading the output


class MyStringable {
public function __toString(): string {
throw new Exception("exception!");
}
}

try {
test([
"foo" => new MyStringable,
]);
} catch (Throwable $e) {
echo $e->getMessage(), "\n";
}

echo "--- Exception from warning should abort execution ---\n";

set_error_handler(function($errno, $errstr) {
throw new Exception($errstr);
}, E_WARNING);

try {
test([
"foo" => [],
"foo2" => [],
]);
} catch (Throwable $e) {
echo $e->getMessage(), "\n";
}

set_error_handler(null, E_WARNING);

echo "--- Warning may continue execution ---\n";

try {
test([
"foo" => [],
"foo2" => [],
]);
} catch (Throwable $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECTF--
--- Invalid key ---
XSLTProcessor::setParameter(): Argument #2 ($name) must contain only string keys
--- Valid key and value, but special cases ---

1
--- Exception from Stringable should abort execution ---
exception!
--- Exception from warning should abort execution ---
Array to string conversion
--- Warning may continue execution ---

Warning: Array to string conversion in %s on line %d

Warning: Array to string conversion in %s on line %d
Array
5 changes: 3 additions & 2 deletions ext/xsl/tests/xsltprocessor_setparameter-errorquote.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ $proc->importStylesheet($xsl);
$proc->setParameter('', '', '"\'');
$proc->transformToXml($dom);
?>
--EXPECTF--
Warning: XSLTProcessor::transformToXml(): Cannot create XPath expression (string contains both quote and double-quotes) in %s on line %d
Done
--EXPECT--
Done
--CREDITS--
Christian Weiske, [email protected]
PHP Testfest Berlin 2009-05-09
95 changes: 22 additions & 73 deletions ext/xsl/xsltprocessor.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,75 +21,29 @@

#include "php.h"
#include "php_xsl.h"
#include <libxslt/variables.h>
#include "ext/libxml/php_libxml.h"

/* {{{ php_xsl_xslt_string_to_xpathexpr()
Translates a string to a XPath Expression */
static char *php_xsl_xslt_string_to_xpathexpr(const char *str)
{
const xmlChar *string = (const xmlChar *)str;

xmlChar *value;
int str_len;

str_len = xmlStrlen(string) + 3;

if (xmlStrchr(string, '"')) {
if (xmlStrchr(string, '\'')) {
php_error_docref(NULL, E_WARNING, "Cannot create XPath expression (string contains both quote and double-quotes)");
return NULL;
}
value = (xmlChar*) safe_emalloc (str_len, sizeof(xmlChar), 0);
snprintf((char*)value, str_len, "'%s'", string);
} else {
value = (xmlChar*) safe_emalloc (str_len, sizeof(xmlChar), 0);
snprintf((char *)value, str_len, "\"%s\"", string);
}
return (char *) value;
}
/* }}} */

/* {{{ php_xsl_xslt_make_params()
Translates a PHP array to a libxslt parameters array */
static char **php_xsl_xslt_make_params(HashTable *parht, int xpath_params)
static zend_result php_xsl_xslt_apply_params(xsltTransformContextPtr ctxt, HashTable *params)
{

int parsize;
zval *value;
char *xpath_expr;
zend_string *string_key;
char **params = NULL;
int i = 0;

parsize = (2 * zend_hash_num_elements(parht) + 1) * sizeof(char *);
params = (char **)safe_emalloc((2 * zend_hash_num_elements(parht) + 1), sizeof(char *), 0);
memset((char *)params, 0, parsize);
zval *value;

ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(parht, string_key, value) {
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(params, string_key, value) {
ZEND_ASSERT(string_key != NULL);
if (Z_TYPE_P(value) != IS_STRING) {
if (!try_convert_to_string(value)) {
efree(params);
return NULL;
}
}
/* Already a string because of setParameter() */
ZEND_ASSERT(Z_TYPE_P(value) == IS_STRING);

if (!xpath_params) {
xpath_expr = php_xsl_xslt_string_to_xpathexpr(Z_STRVAL_P(value));
} else {
xpath_expr = estrndup(Z_STRVAL_P(value), Z_STRLEN_P(value));
}
if (xpath_expr) {
params[i++] = estrndup(ZSTR_VAL(string_key), ZSTR_LEN(string_key));
params[i++] = xpath_expr;
int result = xsltQuoteOneUserParam(ctxt, (const xmlChar *) ZSTR_VAL(string_key), (const xmlChar *) Z_STRVAL_P(value));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this potentially cause issues with values (or even keys...) that have a nul byte? Seems with the manual quoting this would have been taken care of?

Maybe this needs to be checked in setParameter() that the provided strings do not contain nul bytes?

Copy link
Member Author

@nielsdos nielsdos Sep 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this causes issues with NUL bytes.

Seems with the manual quoting this would have been taken care of?

Nope, even today if you do this:

$xslt->setParameter('', 'foo', "a\0b");
var_dump($xslt->getParameter('', 'foo'));

you get the output string(3) "ab", but only the letter a is outputted in the XML document after XSL transformation.

Maybe this needs to be checked in setParameter() that the provided strings do not contain nul bytes?

That would be best.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok implemented the null byte check now

if (result < 0) {
php_error_docref(NULL, E_WARNING, "Could not apply parameter '%s'", ZSTR_VAL(string_key));
return FAILURE;
}
} ZEND_HASH_FOREACH_END();

params[i++] = NULL;

return params;
return SUCCESS;
}
/* }}} */

static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int type) /* {{{ */
{
Expand Down Expand Up @@ -402,8 +356,6 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl
xmlNodePtr node = NULL;
xsltTransformContextPtr ctxt;
php_libxml_node_object *object;
char **params = NULL;
int clone;
zval *doXInclude, rv;
zend_string *member;
FILE *f;
Expand Down Expand Up @@ -440,10 +392,6 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl
f = NULL;
}

if (intern->parameter) {
params = php_xsl_xslt_make_params(intern->parameter, 0);
}

intern->doc = emalloc(sizeof(php_libxml_node_object));
memset(intern->doc, 0, sizeof(php_libxml_node_object));

Expand All @@ -459,6 +407,13 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl
ctxt = xsltNewTransformContext(style, doc);
ctxt->_private = (void *) intern;

if (intern->parameter) {
zend_result status = php_xsl_xslt_apply_params(ctxt, intern->parameter);
if (UNEXPECTED(status != SUCCESS) && EG(exception)) {
goto out;
}
}

member = ZSTR_INIT_LITERAL("doXInclude", 0);
doXInclude = zend_std_read_property(Z_OBJ_P(id), member, BP_VAR_IS, NULL, &rv);
if (Z_TYPE_P(doXInclude) != IS_NULL) {
Expand Down Expand Up @@ -506,8 +461,10 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl
if (secPrefsError == 1) {
php_error_docref(NULL, E_WARNING, "Can't set libxslt security properties, not doing transformation for security reasons");
} else {
newdocp = xsltApplyStylesheetUser(style, doc, (const char**) params, NULL, f, ctxt);
newdocp = xsltApplyStylesheetUser(style, doc, /* params (handled manually) */ NULL, /* output */ NULL, f, ctxt);
}

out:
if (f) {
fclose(f);
}
Expand All @@ -527,14 +484,6 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl
efree(intern->doc);
intern->doc = NULL;

if (params) {
clone = 0;
while(params[clone]) {
efree(params[clone++]);
}
efree(params);
}

return newdocp;

}
Expand Down Expand Up @@ -737,7 +686,7 @@ PHP_METHOD(XSLTProcessor, getParameter)
}
intern = Z_XSL_P(id);
if ((value = zend_hash_find(intern->parameter, name)) != NULL) {
RETURN_STR(zval_get_string(value));
RETURN_STR_COPY(Z_STR_P(value));
} else {
RETURN_FALSE;
}
Expand Down