desktop/source/lib/init.cxx | 3 embeddedobj/source/commonembedding/miscobj.cxx | 9 + include/tools/hostfilter.hxx | 10 ++ sc/source/core/tool/webservicelink.cxx | 6 + sc/source/ui/dataprovider/dataprovider.cxx | 7 + sc/source/ui/docshell/arealink.cxx | 7 + sc/source/ui/docshell/externalrefmgr.cxx | 6 + sc/source/ui/docshell/tablink.cxx | 7 + sd/source/ui/dlg/tpaction.cxx | 11 ++ sfx2/source/appl/appopen.cxx | 7 + sfx2/source/appl/fileobj.cxx | 7 + sw/source/core/docnode/section.cxx | 7 + tools/CppunitTest_tools_test.mk | 1 tools/qa/cppunit/test_hostfilter.cxx | 120 +++++++++++++++++++++++++ tools/source/inet/hostfilter.cxx | 68 ++++++++++++++ 15 files changed, 275 insertions(+), 1 deletion(-)
New commits: commit c1620c70dd044b51e8e3829bd9f7372e2c98d98c Author: Caolán <[email protected]> AuthorDate: Wed Feb 25 09:06:22 2026 +0000 Commit: Caolán McNamara <[email protected]> CommitDate: Thu Mar 5 14:12:59 2026 +0100 Add an advisory setAllowedExtRefPaths Not intended to apply a strict jail, but an advisory list of paths that are meaningful to allow links to and update from. Change-Id: I46742f05ee87194aea196e95f0a7fbb762634f5f Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200866 Reviewed-by: Miklos Vajna <[email protected]> Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201004 Reviewed-by: Caolán McNamara <[email protected]> diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index e58ad847209b..bbc479299a5a 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -8499,6 +8499,9 @@ static int lo_initialize(LibreOfficeKit* pThis, const char* pAppPath, const char if (const char* pHostExemptVerifyHost = ::getenv("LOK_HOST_ALLOWLIST_EXEMPT_VERIFY_HOST")) HostFilter::setAllowedHostsExemptVerifyHost(strncmp(pHostExemptVerifyHost,"1", 1) == 0); + if (const char* pExtRefPaths = ::getenv("LOK_ALLOWED_EXTREF_PATHS")) + HostFilter::setAllowedExtRefPaths(pExtRefPaths); + // What stage are we at ? if (pThis == nullptr) { diff --git a/embeddedobj/source/commonembedding/miscobj.cxx b/embeddedobj/source/commonembedding/miscobj.cxx index fa6a725afb47..ef31ff7b4695 100644 --- a/embeddedobj/source/commonembedding/miscobj.cxx +++ b/embeddedobj/source/commonembedding/miscobj.cxx @@ -28,6 +28,7 @@ #include <com/sun/star/beans/NamedValue.hpp> #include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/io/IOException.hpp> #include <com/sun/star/io/TempFile.hpp> #include <comphelper/multicontainer2.hxx> @@ -45,6 +46,7 @@ #include <comphelper/diagnose_ex.hxx> #include <cppuhelper/supportsservice.hxx> #include <comphelper/sequenceashashmap.hxx> +#include <tools/hostfilter.hxx> #include "persistence.hxx" @@ -182,6 +184,13 @@ void OCommonEmbeddedObject::LinkInit_Impl( OSL_ENSURE( m_aLinkURL.getLength() && m_aLinkFilterName.getLength(), "Filter and URL must be provided!" ); + if (HostFilter::isFileUrlForbidden(m_aLinkURL)) + { + SAL_WARN("embeddedobj.common", "LinkInit_Impl: blocked file path: \"" << m_aLinkURL << "\""); + m_aLinkURL.clear(); + throw io::IOException(u"blocked external reference path"_ustr); + } + m_bReadOnly = true; if ( m_aLinkFilterName.getLength() ) { diff --git a/include/tools/hostfilter.hxx b/include/tools/hostfilter.hxx index 8e992b407bbb..423968f08cc1 100644 --- a/include/tools/hostfilter.hxx +++ b/include/tools/hostfilter.hxx @@ -27,6 +27,16 @@ public: static void setExemptVerifyHost(const OUString& rExemptVerifyHost); static bool isExemptVerifyHost(const std::u16string_view rHost); + + /// A colon-separated list of directory paths that file:// external + /// references are allowed to reach. An empty string means: + /// "block all file URLs" + static void setAllowedExtRefPaths(const char* sPaths); + + /// Return true when rFileUrl is a file:// URL that is outside any + /// directory registered with setAllowedExtRefPaths. Non-file URLs + /// are always allowed. + static bool isFileUrlForbidden(const OUString& rFileUrl); }; #endif diff --git a/sc/source/core/tool/webservicelink.cxx b/sc/source/core/tool/webservicelink.cxx index c30f34300edf..ba3e6f162f83 100644 --- a/sc/source/core/tool/webservicelink.cxx +++ b/sc/source/core/tool/webservicelink.cxx @@ -48,6 +48,12 @@ sfx2::SvBaseLink::UpdateResult ScWebServiceLink::DataChanged(const OUString&, co return ERROR_GENERAL; } + if (HostFilter::isFileUrlForbidden(aURL)) + { + SAL_WARN("sc.ui", "ScWebServiceLink::DataChanged: blocked file path: \"" << aURL << "\""); + return ERROR_GENERAL; + } + css::uno::Reference<css::ucb::XSimpleFileAccess3> xFileAccess = css::ucb::SimpleFileAccess::create(comphelper::getProcessComponentContext()); if (!xFileAccess.is()) diff --git a/sc/source/ui/dataprovider/dataprovider.cxx b/sc/source/ui/dataprovider/dataprovider.cxx index 1a31a1afddaf..20b59c3c7e8d 100644 --- a/sc/source/ui/dataprovider/dataprovider.cxx +++ b/sc/source/ui/dataprovider/dataprovider.cxx @@ -17,6 +17,7 @@ #include <unotools/charclass.hxx> #include <tools/stream.hxx> #include <comphelper/processfactory.hxx> +#include <tools/hostfilter.hxx> #include "htmldataprovider.hxx" #include "xmldataprovider.hxx" @@ -32,6 +33,12 @@ namespace sc { std::unique_ptr<SvStream> DataProvider::FetchStreamFromURL(const OUString& rURL, OStringBuffer& rBuffer) { + if (HostFilter::isFileUrlForbidden(rURL)) + { + SAL_WARN("sc.ui", "DataProvider::FetchStreamFromURL: blocked file path: \"" << rURL << "\""); + return nullptr; + } + try { uno::Reference< ucb::XSimpleFileAccess3 > xFileAccess = ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ); diff --git a/sc/source/ui/docshell/arealink.cxx b/sc/source/ui/docshell/arealink.cxx index 95f7f04d3f02..279698187aa1 100644 --- a/sc/source/ui/docshell/arealink.cxx +++ b/sc/source/ui/docshell/arealink.cxx @@ -45,6 +45,7 @@ #include <scabstdlg.hxx> #include <clipparam.hxx> +#include <tools/hostfilter.hxx> ScAreaLink::ScAreaLink( ScDocShell& rShell, OUString aFile, @@ -232,6 +233,12 @@ bool ScAreaLink::Refresh( const OUString& rNewFile, const OUString& rNewFilter, OUString aNewUrl( ScGlobal::GetAbsDocName( rNewFile, &m_rDocSh ) ); bool bNewUrlName = (aNewUrl != aFileName); + if (HostFilter::isFileUrlForbidden(aNewUrl)) + { + SAL_WARN("sc.ui", "ScAreaLink::Refresh: blocked file path: \"" << aNewUrl << "\""); + return false; + } + std::shared_ptr<const SfxFilter> pFilter = m_rDocSh.GetFactory().GetFilterContainer()->GetFilter4FilterName(rNewFilter); if (!pFilter) return false; diff --git a/sc/source/ui/docshell/externalrefmgr.cxx b/sc/source/ui/docshell/externalrefmgr.cxx index d9f6639f1794..67a67b8f9ab9 100644 --- a/sc/source/ui/docshell/externalrefmgr.cxx +++ b/sc/source/ui/docshell/externalrefmgr.cxx @@ -2535,6 +2535,12 @@ SfxObjectShellRef ScExternalRefManager::loadSrcDocument(sal_uInt16 nFileId, OUSt return nullptr; } + if (HostFilter::isFileUrlForbidden(aFile)) + { + SAL_WARN( "sc.ui", "ScExternalRefManager::loadSrcDocument: blocked access to local file: \"" << aFile << "\""); + return nullptr; + } + OUString aOptions = pFileData->maFilterOptions; if ( !pFileData->maFilterName.isEmpty() ) rFilter = pFileData->maFilterName; // don't overwrite stored filter with guessed filter diff --git a/sc/source/ui/docshell/tablink.cxx b/sc/source/ui/docshell/tablink.cxx index 4babed2103ed..47fb54b33bf7 100644 --- a/sc/source/ui/docshell/tablink.cxx +++ b/sc/source/ui/docshell/tablink.cxx @@ -49,6 +49,7 @@ #include <global.hxx> #include <hints.hxx> #include <dociter.hxx> +#include <tools/hostfilter.hxx> #include <formula/opcode.hxx> #include <formulaiter.hxx> #include <tokenarray.hxx> @@ -142,6 +143,12 @@ bool ScTableLink::Refresh(const OUString& rNewFile, const OUString& rNewFilter, OUString aNewUrl = ScGlobal::GetAbsDocName(rNewFile, &m_rDocSh); bool bNewUrlName = aFileName != aNewUrl; + if (HostFilter::isFileUrlForbidden(aNewUrl)) + { + SAL_WARN("sc.ui", "ScTableLink::Refresh: blocked file path: \"" << aNewUrl << "\""); + return false; + } + std::shared_ptr<const SfxFilter> pFilter = m_rDocSh.GetFactory().GetFilterContainer()->GetFilter4FilterName(rNewFilter); if (!pFilter) return false; diff --git a/sd/source/ui/dlg/tpaction.cxx b/sd/source/ui/dlg/tpaction.cxx index 8f3db490d4eb..125acd7a909f 100644 --- a/sd/source/ui/dlg/tpaction.cxx +++ b/sd/source/ui/dlg/tpaction.cxx @@ -32,6 +32,7 @@ #include <sfx2/strings.hrc> #include <o3tl/safeint.hxx> #include <tools/debug.hxx> +#include <tools/hostfilter.hxx> #include <sfx2/app.hxx> #include <svx/svdograf.hxx> #include <svl/stritem.hxx> @@ -775,7 +776,15 @@ OUString SdTPAction::GetEditText( bool bFullDocDestination ) aBaseURL = mpDoc->GetDocSh()->GetMedium()->GetBaseURL(); if( !aStr.isEmpty() && aURL.GetProtocol() == INetProtocol::NotValid ) - aURL = INetURLObject( ::URIHelper::SmartRel2Abs( INetURLObject(aBaseURL), aStr, URIHelper::GetMaybeFileHdl() ) ); + { + INetURLObject aResult( ::URIHelper::SmartRel2Abs( INetURLObject(aBaseURL), aStr, URIHelper::GetMaybeFileHdl() ) ); + + OUString sCandidate = aResult.GetMainURL(INetURLObject::DecodeMechanism::NONE); + if (!HostFilter::isFileUrlForbidden(sCandidate)) + aURL = aResult; + else + SAL_WARN( "sd", "SdTPAction::FillItemSet: blocked display of local file: \"" << sCandidate << "\""); + } // get adjusted file name aStr = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); diff --git a/sfx2/source/appl/appopen.cxx b/sfx2/source/appl/appopen.cxx index 457cda2bc9a1..b35f87c6ca3d 100644 --- a/sfx2/source/appl/appopen.cxx +++ b/sfx2/source/appl/appopen.cxx @@ -89,6 +89,7 @@ #include <sfx2/sfxsids.hrc> #include <o3tl/string_view.hxx> #include <openuriexternally.hxx> +#include <tools/hostfilter.hxx> #include <officecfg/Office/ProtocolHandler.hxx> #include <officecfg/Office/Security.hxx> @@ -790,6 +791,12 @@ void SfxApplication::OpenDocExec_Impl( SfxRequest& rReq ) assert(pFileName && "SID_FILE_NAME is required"); OUString aFileName = pFileName->GetValue(); + if (HostFilter::isFileUrlForbidden(aFileName)) + { + SAL_WARN("sfx.appl", "SID_OPENDOC: blocked file path: \"" << aFileName << "\""); + return; + } + OUString aReferer; const SfxStringItem* pRefererItem = rReq.GetArg(SID_REFERER); if ( pRefererItem ) diff --git a/sfx2/source/appl/fileobj.cxx b/sfx2/source/appl/fileobj.cxx index 1e8517acddb6..258abadf4cc3 100644 --- a/sfx2/source/appl/fileobj.cxx +++ b/sfx2/source/appl/fileobj.cxx @@ -36,6 +36,7 @@ #include <sfx2/opengrf.hxx> #include <sfx2/sfxresid.hxx> #include <sfx2/objsh.hxx> +#include <tools/hostfilter.hxx> #include "fileobj.hxx" #include <sfx2/strings.hrc> #include <vcl/svapp.hxx> @@ -156,6 +157,12 @@ bool SvFileObject::LoadFile_Impl() if( bWaitForData || !bLoadAgain || xMed.is() ) return false; + if (HostFilter::isFileUrlForbidden(sFileNm)) + { + SAL_WARN("sfx.appl", "SvFileObject::LoadFile_Impl: blocked file path: \"" << sFileNm << "\""); + return false; + } + // at the moment on the current DocShell xMed = new SfxMedium( sFileNm, sReferer, StreamMode::STD_READ ); SvLinkSource::StreamToLoadFrom aStreamToLoadFrom = diff --git a/sw/source/core/docnode/section.cxx b/sw/source/core/docnode/section.cxx index 13e61eaf0caf..acf3c6e469a6 100644 --- a/sw/source/core/docnode/section.cxx +++ b/sw/source/core/docnode/section.cxx @@ -63,6 +63,7 @@ #include <unosection.hxx> #include <calbck.hxx> #include <fmtclds.hxx> +#include <tools/hostfilter.hxx> #include <algorithm> #include <utility> #include "ndsect.hxx" @@ -1187,6 +1188,12 @@ static void lcl_UpdateLinksInSect( const SwBaseLink& rUpdLnk, SwSectionNode& rSe sfx2::LinkManager::GetDisplayNames( this, nullptr, &sFileName, &sRange, &sFilter ); + if (HostFilter::isFileUrlForbidden(sFileName)) + { + SAL_WARN("sw.core", "SwIntrnlSectRefLink::DataChanged: blocked file path: \"" << sFileName << "\""); + break; + } + RedlineFlags eOldRedlineFlags = RedlineFlags::NONE; SfxObjectShellRef xDocSh; SfxObjectShellLock xLockRef; diff --git a/tools/CppunitTest_tools_test.mk b/tools/CppunitTest_tools_test.mk index f5422b78e74f..4fda305a313f 100644 --- a/tools/CppunitTest_tools_test.mk +++ b/tools/CppunitTest_tools_test.mk @@ -37,6 +37,7 @@ $(eval $(call gb_CppunitTest_add_exception_objects,tools_test, \ tools/qa/cppunit/test_cpu_runtime_detection_SSE2 \ tools/qa/cppunit/test_cpu_runtime_detection_SSSE3 \ tools/qa/cppunit/test_Wildcard \ + tools/qa/cppunit/test_hostfilter \ tools/qa/cppunit/test_zcodec \ )) diff --git a/tools/qa/cppunit/test_hostfilter.cxx b/tools/qa/cppunit/test_hostfilter.cxx new file mode 100644 index 000000000000..3a2a34e5bfa1 --- /dev/null +++ b/tools/qa/cppunit/test_hostfilter.cxx @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> + +#include <cppunit/TestAssert.h> +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> +#include <tools/hostfilter.hxx> + +namespace +{ +class TestHostFilter : public CppUnit::TestFixture +{ +public: + void testEmptyAllowlist(); + void testNonFileUrl(); + void testAllowedPath(); + void testSiblingDirectoryNotAllowed(); + void testParentDirectoryNotAllowed(); + void testMultiplePaths(); + void testEncodedFileUrl(); + void testParentDirectorySegments(); + + CPPUNIT_TEST_SUITE(TestHostFilter); + CPPUNIT_TEST(testEmptyAllowlist); + CPPUNIT_TEST(testNonFileUrl); + CPPUNIT_TEST(testAllowedPath); + CPPUNIT_TEST(testSiblingDirectoryNotAllowed); + CPPUNIT_TEST(testParentDirectoryNotAllowed); + CPPUNIT_TEST(testMultiplePaths); + CPPUNIT_TEST(testEncodedFileUrl); + CPPUNIT_TEST(testParentDirectorySegments); + CPPUNIT_TEST_SUITE_END(); +}; + +void TestHostFilter::testEmptyAllowlist() +{ + // empty string means "block all file URLs" + HostFilter::setAllowedExtRefPaths(""); + CPPUNIT_ASSERT(HostFilter::isFileUrlForbidden(u"file:///home/user/doc.ods"_ustr)); + CPPUNIT_ASSERT(HostFilter::isFileUrlForbidden(u"file:///tmp/doc.ods"_ustr)); +} + +void TestHostFilter::testNonFileUrl() +{ + HostFilter::setAllowedExtRefPaths(""); + // non-file URLs are not subject to the extref path allowlist + CPPUNIT_ASSERT(!HostFilter::isFileUrlForbidden(u"http://example.com/doc.ods"_ustr)); + CPPUNIT_ASSERT(!HostFilter::isFileUrlForbidden(u"https://example.com/doc.ods"_ustr)); + CPPUNIT_ASSERT(!HostFilter::isFileUrlForbidden(u"ftp://example.com/doc.ods"_ustr)); +} + +void TestHostFilter::testAllowedPath() +{ + HostFilter::setAllowedExtRefPaths("/tmp/docs"); + // file inside allowed directory is permitted + CPPUNIT_ASSERT(!HostFilter::isFileUrlForbidden(u"file:///tmp/docs/sheet.ods"_ustr)); + // file in a subdirectory is permitted + CPPUNIT_ASSERT(!HostFilter::isFileUrlForbidden(u"file:///tmp/docs/sub/sheet.ods"_ustr)); + // file outside allowed directory is blocked + CPPUNIT_ASSERT(HostFilter::isFileUrlForbidden(u"file:///home/user/doc.ods"_ustr)); +} + +void TestHostFilter::testSiblingDirectoryNotAllowed() +{ + // /tmp/user must not match /tmp/username + HostFilter::setAllowedExtRefPaths("/tmp/user"); + CPPUNIT_ASSERT(!HostFilter::isFileUrlForbidden(u"file:///tmp/user/doc.ods"_ustr)); + CPPUNIT_ASSERT(HostFilter::isFileUrlForbidden(u"file:///tmp/username/doc.ods"_ustr)); + CPPUNIT_ASSERT(HostFilter::isFileUrlForbidden(u"file:///tmp/usera/doc.ods"_ustr)); +} + +void TestHostFilter::testParentDirectoryNotAllowed() +{ + HostFilter::setAllowedExtRefPaths("/tmp/docs"); + // parent directory is not allowed + CPPUNIT_ASSERT(HostFilter::isFileUrlForbidden(u"file:///tmp/secret.ods"_ustr)); + // unrelated path is not allowed + CPPUNIT_ASSERT(HostFilter::isFileUrlForbidden(u"file:///var/data/doc.ods"_ustr)); +} + +void TestHostFilter::testMultiplePaths() +{ + // colon-separated list of allowed paths + HostFilter::setAllowedExtRefPaths("/tmp/a:/tmp/b"); + CPPUNIT_ASSERT(!HostFilter::isFileUrlForbidden(u"file:///tmp/a/doc.ods"_ustr)); + CPPUNIT_ASSERT(!HostFilter::isFileUrlForbidden(u"file:///tmp/b/doc.ods"_ustr)); + CPPUNIT_ASSERT(HostFilter::isFileUrlForbidden(u"file:///tmp/c/doc.ods"_ustr)); +} + +void TestHostFilter::testEncodedFileUrl() +{ + HostFilter::setAllowedExtRefPaths("/tmp/my docs"); + // percent-encoded space in URL should match allowed path with space + CPPUNIT_ASSERT(!HostFilter::isFileUrlForbidden(u"file:///tmp/my%20docs/sheet.ods"_ustr)); + CPPUNIT_ASSERT(!HostFilter::isFileUrlForbidden(u"file:///tmp/my%20docs/sub/sheet.ods"_ustr)); + // encoded URL outside allowed path is still blocked + CPPUNIT_ASSERT(HostFilter::isFileUrlForbidden(u"file:///tmp/other%20docs/sheet.ods"_ustr)); +} + +void TestHostFilter::testParentDirectorySegments() +{ + HostFilter::setAllowedExtRefPaths("/tmp/docs"); + // .. segments that escape the allowed directory should be blocked + CPPUNIT_ASSERT(HostFilter::isFileUrlForbidden(u"file:///tmp/docs/../other/sheet.ods"_ustr)); + // .. segments that stay within the allowed directory are permitted + CPPUNIT_ASSERT(!HostFilter::isFileUrlForbidden(u"file:///tmp/docs/sub/../sheet.ods"_ustr)); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(TestHostFilter); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/tools/source/inet/hostfilter.cxx b/tools/source/inet/hostfilter.cxx index 75ade47489af..99ddb750dd4c 100644 --- a/tools/source/inet/hostfilter.cxx +++ b/tools/source/inet/hostfilter.cxx @@ -8,7 +8,9 @@ */ #include <tools/hostfilter.hxx> +#include <osl/file.hxx> #include <regex> +#include <vector> static std::regex g_AllowedHostsRegex(""); static OUString g_ExceptVerifyHost; @@ -51,4 +53,70 @@ bool HostFilter::isExemptVerifyHost(const std::u16string_view rHost) return false; } +static bool g_AllowedExtRefPathsConfigured = false; +static std::vector<OUString> g_AllowedExtRefPaths; + +void HostFilter::setAllowedExtRefPaths(const char* sPaths) +{ + g_AllowedExtRefPathsConfigured = true; + g_AllowedExtRefPaths.clear(); + + if (!sPaths || sPaths[0] == ' + return; + + OString sPathList(sPaths); + sal_Int32 nIndex = 0; + do + { + OString sPath = sPathList.getToken(0, ':', nIndex); + if (sPath.isEmpty()) + continue; + + OUString aSysPath = OStringToOUString(sPath, RTL_TEXTENCODING_UTF8); + OUString aFileUrl; + if (osl::FileBase::getFileURLFromSystemPath(aSysPath, aFileUrl) != osl::FileBase::E_None) + continue; + + // Normalize relative paths and .. segments (does not resolve symlinks) + OUString aNormalized; + if (osl::FileBase::getAbsoluteFileURL(OUString(), aFileUrl, aNormalized) + == osl::FileBase::E_None) + { + if (!aNormalized.endsWith("/")) + aNormalized += "/"; + g_AllowedExtRefPaths.push_back(aNormalized); + } + else + { + if (!aFileUrl.endsWith("/")) + aFileUrl += "/"; + g_AllowedExtRefPaths.push_back(aFileUrl); + } + } while (nIndex >= 0); +} + +bool HostFilter::isFileUrlForbidden(const OUString& rFileUrl) +{ + if (!g_AllowedExtRefPathsConfigured) + return false; + + if (!rFileUrl.startsWithIgnoreAsciiCase("file:")) + return false; + + // Normalize relative paths and .. segments (does not resolve symlinks) + OUString aNormalized; + if (osl::FileBase::getAbsoluteFileURL(OUString(), rFileUrl, aNormalized) + != osl::FileBase::E_None) + return true; + + // Case-sensitive comparison: assumes a case-sensitive filesystem (i.e. Linux). + for (const auto& rAllowed : g_AllowedExtRefPaths) + { + if (aNormalized.startsWith(rAllowed)) + return false; + } + + return true; +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
