xslt filter: set every libxslt transformation option explicitly
libxslt leaves each option unset on a fresh transformation context and takes its own default for it. The service registers no libxml input handlers, and a stylesheet only has to produce the result tree, so set all of them and allow back local file reads alone, which is how a stylesheet picks up a second file beside the one it converts. Signed-off-by: Caolán McNamara <caolan.mcnamara@collabora.com> Change-Id: I58abfc3919e26683d3fe2de72362f6c453eef8cf Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/9939 Tested-by: Jenkins CPCI <releng@collaboraoffice.com> Reviewed-by: Noel Grandin <noel.grandin@collabora.com> (cherry picked from commit 02da3650b1bb71170112a480c7fa0220a2a34490) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/210434 Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org> Tested-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
committed by
Xisco Fauli
parent
f8ed170f27
commit
d5d950c2b9
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xsl:stylesheet version="1.0"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:exsl="http://exslt.org/common"
|
||||
extension-element-prefixes="exsl">
|
||||
|
||||
<xsl:param name="targetBaseURL"/>
|
||||
|
||||
<xsl:template match="/">
|
||||
<exsl:document href="{concat($targetBaseURL, 'xslt-write-test.txt')}" method="text">
|
||||
<xsl:text>written</xsl:text>
|
||||
</exsl:document>
|
||||
<xsl:copy-of select="/"/>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
||||
@@ -44,27 +44,38 @@ class XsltFilterTest
|
||||
public:
|
||||
void testXsltCopyOld();
|
||||
void testXsltCopyNew();
|
||||
void testXsltOutputStreamOnly();
|
||||
|
||||
CPPUNIT_TEST_SUITE(XsltFilterTest);
|
||||
CPPUNIT_TEST(testXsltCopyOld);
|
||||
CPPUNIT_TEST(testXsltCopyNew);
|
||||
CPPUNIT_TEST(testXsltOutputStreamOnly);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
};
|
||||
|
||||
class Listener : public ::cppu::WeakImplHelper<io::XStreamListener>
|
||||
{
|
||||
public:
|
||||
Listener() : m_bDone(false) {}
|
||||
/// Pass false for bFailOnError when the transformation is meant to fail.
|
||||
Listener(bool bFailOnError = true)
|
||||
: m_bDone(false), m_bFailOnError(bFailOnError), m_bError(false) {}
|
||||
|
||||
void wait() {
|
||||
std::unique_lock<std::mutex> g(m_mutex);
|
||||
m_cond.wait(g, [this]() { return m_bDone; });
|
||||
}
|
||||
|
||||
bool hadError() {
|
||||
std::scoped_lock<std::mutex> g(m_mutex);
|
||||
return m_bError;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_cond;
|
||||
bool m_bDone;
|
||||
bool m_bFailOnError;
|
||||
bool m_bError;
|
||||
|
||||
virtual void SAL_CALL disposing(const lang::EventObject&) noexcept override {}
|
||||
virtual void SAL_CALL started() noexcept override {}
|
||||
@@ -72,9 +83,16 @@ private:
|
||||
virtual void SAL_CALL terminated() noexcept override { notifyDone(); }
|
||||
virtual void SAL_CALL error(const uno::Any& e) override
|
||||
{
|
||||
{
|
||||
std::scoped_lock<std::mutex> g(m_mutex);
|
||||
m_bError = true;
|
||||
}
|
||||
notifyDone(); // set on error too, otherwise main thread waits forever
|
||||
SAL_WARN("filter.xslt", e);
|
||||
CPPUNIT_FAIL("exception while in XSLT");
|
||||
// A failure thrown here would land on the transformation thread, so record it and let
|
||||
// the test body check hadError.
|
||||
if (m_bFailOnError)
|
||||
CPPUNIT_FAIL("exception while in XSLT");
|
||||
}
|
||||
|
||||
void notifyDone() {
|
||||
@@ -198,6 +216,63 @@ void XsltFilterTest::testXsltCopyOld()
|
||||
osl_removeFile(tempURL.pData);
|
||||
}
|
||||
|
||||
// The transformation writes to the output stream it is given, and nowhere else.
|
||||
void XsltFilterTest::testXsltOutputStreamOnly()
|
||||
{
|
||||
OUString tempDirURL;
|
||||
osl_getTempDirURL(&tempDirURL.pData);
|
||||
oslFileHandle tempFile;
|
||||
OUString tempURL;
|
||||
osl::File::RC rc = osl::File::createTempFile(nullptr, &tempFile, &tempURL);
|
||||
CPPUNIT_ASSERT_EQUAL(osl::FileBase::E_None, rc);
|
||||
osl_closeFile(tempFile); // close it so xSFA can open it on WNT
|
||||
|
||||
OUString writtenURL(tempDirURL + "/xslt-write-test.txt");
|
||||
osl_removeFile(writtenURL.pData);
|
||||
|
||||
OUString source(
|
||||
m_directories.getURLFromSrc(u"/filter/source/xsltfilter/xsltfilter.component"));
|
||||
uno::Sequence<uno::Any> args{
|
||||
uno::Any(beans::NamedValue(u"StylesheetURL"_ustr,
|
||||
uno::Any(m_directories.getURLFromSrc(u"/filter/qa/cppunit/data/xslt/document.xslt")))),
|
||||
uno::Any(beans::NamedValue(u"SourceURL"_ustr, uno::Any(source))),
|
||||
uno::Any(beans::NamedValue(u"TargetURL"_ustr, uno::Any(tempURL))),
|
||||
uno::Any(beans::NamedValue(u"TargetBaseURL"_ustr, uno::Any(tempDirURL + "/"))),
|
||||
uno::Any(beans::NamedValue(u"SystemType"_ustr, uno::Any(OUString()))),
|
||||
uno::Any(beans::NamedValue(u"PublicType"_ustr, uno::Any(OUString())))
|
||||
};
|
||||
|
||||
uno::Reference<ucb::XSimpleFileAccess3> xSFA =
|
||||
ucb::SimpleFileAccess::create(getComponentContext());
|
||||
|
||||
uno::Reference<io::XInputStream> xIn = xSFA->openFileRead(source);
|
||||
uno::Reference<io::XOutputStream> xOut = xSFA->openFileWrite(tempURL);
|
||||
|
||||
rtl::Reference<Listener> xListener = new Listener(false);
|
||||
|
||||
uno::Reference<xml::xslt::XXSLTTransformer> xXslt(
|
||||
xml::xslt::XSLTTransformer::create(getComponentContext(), args));
|
||||
|
||||
xXslt->addListener(xListener);
|
||||
xXslt->setInputStream(xIn);
|
||||
xXslt->setOutputStream(xOut);
|
||||
|
||||
xXslt->start();
|
||||
|
||||
xListener->wait();
|
||||
|
||||
xIn->closeInput();
|
||||
xOut->closeOutput();
|
||||
xXslt->terminate();
|
||||
|
||||
CPPUNIT_ASSERT_MESSAGE("the transformation wrote outside its output stream",
|
||||
!xSFA->exists(writtenURL));
|
||||
CPPUNIT_ASSERT_MESSAGE("the transformation was not reported as failed",
|
||||
xListener->hadError());
|
||||
|
||||
osl_removeFile(tempURL.pData);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(XsltFilterTest);
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <libxml/xpath.h>
|
||||
#include <libxml/xpathInternals.h>
|
||||
#include <libxml/xmlstring.h>
|
||||
#include <libxslt/security.h>
|
||||
#include <libxslt/transform.h>
|
||||
#include <libxslt/xsltutils.h>
|
||||
#include <libxslt/variables.h>
|
||||
@@ -307,6 +308,7 @@ namespace XSLT
|
||||
xsltDebugDumpExtensions(NULL);
|
||||
#endif
|
||||
std::optional<OleHandler> oh(std::in_place, m_transformer->getComponentContext());
|
||||
xsltSecurityPrefsPtr securityPrefs = nullptr;
|
||||
if (styleSheet)
|
||||
{
|
||||
xsltTransformContextPtr tcontext = xsltNewTransformContext(
|
||||
@@ -317,8 +319,23 @@ namespace XSLT
|
||||
}
|
||||
oh->registercontext(tcontext);
|
||||
xsltQuoteUserParams(tcontext, params.data());
|
||||
result = xsltApplyStylesheetUser(styleSheet, doc, nullptr, nullptr, nullptr,
|
||||
tcontext);
|
||||
// A stylesheet builds the result tree and nothing else. Refuse every option libxslt
|
||||
// has, then allow back the local reads, which is how a stylesheet picks up a second
|
||||
// file that sits beside the one it converts.
|
||||
securityPrefs = xsltNewSecurityPrefs();
|
||||
if (securityPrefs)
|
||||
{
|
||||
for (auto eOption : { XSLT_SECPREF_READ_FILE, XSLT_SECPREF_WRITE_FILE,
|
||||
XSLT_SECPREF_CREATE_DIRECTORY, XSLT_SECPREF_READ_NETWORK,
|
||||
XSLT_SECPREF_WRITE_NETWORK })
|
||||
xsltSetSecurityPrefs(securityPrefs, eOption, xsltSecurityForbid);
|
||||
xsltSetSecurityPrefs(securityPrefs, XSLT_SECPREF_READ_FILE, xsltSecurityAllow);
|
||||
}
|
||||
if (securityPrefs && xsltSetCtxtSecurityPrefs(securityPrefs, tcontext) == 0)
|
||||
result = xsltApplyStylesheetUser(styleSheet, doc, nullptr, nullptr, nullptr,
|
||||
tcontext);
|
||||
else
|
||||
m_transformer->error(u"No security preferences were applied"_ustr);
|
||||
}
|
||||
|
||||
if (result)
|
||||
@@ -351,6 +368,7 @@ namespace XSLT
|
||||
std::swap(m_tcontext, tcontext);
|
||||
}
|
||||
xsltFreeTransformContext(tcontext);
|
||||
xsltFreeSecurityPrefs(securityPrefs);
|
||||
xmlFreeDoc(doc);
|
||||
xmlFreeDoc(result);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user