This is an automated email from the ASF dual-hosted git repository.
leginee pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/openoffice.git
The following commit(s) were added to refs/heads/trunk by this push:
new 137931e438 svtools: one shared policy for following URLs out of
document content
137931e438 is described below
commit 137931e43899a1545869cb6af6ea936d8cee4956
Author: Peter <[email protected]>
AuthorDate: Tue Sep 22 21:34:30 2026 +0200
svtools: one shared policy for following URLs out of document content
Each module that follows a URL named by document content -- linked graphics,
background images, instance data, media -- decided for itself whether to
check anything, and the check that does exist was written out inline at the
call site.
Add svtools/linkpolicy.hxx as the one place that decides, and apply it at
the
load sites in editeng, forms, sfx2, sw, avmedia and svgio. A relative
reference and a file on this machine load as before; anything else asks the
running document, which is the question the LinkManager already asks.
Two places on those paths assumed success and no longer do: a reference that
cannot be resolved, and a graphic that is not there.
Co-Authored-By: Claude Opus 5 <[email protected]>
---
main/avmedia/source/viewer/mediawindow.cxx | 11 +-
.../avmedia/source/viewer/mediawindowbase_impl.cxx | 7 +
main/editeng/source/items/frmitems.cxx | 10 +
main/forms/source/xforms/model.cxx | 9 +
main/sfx2/source/appl/fileobj.cxx | 15 ++
main/sfx2/source/appl/linkmgr2.cxx | 11 +-
main/svgio/source/svgreader/linkedimagepolicy.hxx | 98 ++++++++
main/svgio/source/svgreader/svgimagenode.cxx | 8 +-
main/svtools/Library_svt.mk | 1 +
main/svtools/Package_inc.mk | 1 +
main/svtools/inc/svtools/linkpolicy.hxx | 251 +++++++++++++++++++++
main/svtools/source/graphic/grfmgr.cxx | 31 +--
main/svtools/source/misc/linkpolicy.cxx | 91 ++++++++
main/sw/source/core/graphic/ndgrf.cxx | 17 +-
main/sw/source/core/layout/paintfrm.cxx | 44 +++-
15 files changed, 554 insertions(+), 51 deletions(-)
diff --git a/main/avmedia/source/viewer/mediawindow.cxx
b/main/avmedia/source/viewer/mediawindow.cxx
index 63cbb8d594..182b68ae88 100644
--- a/main/avmedia/source/viewer/mediawindow.cxx
+++ b/main/avmedia/source/viewer/mediawindow.cxx
@@ -32,6 +32,7 @@
#include <unotools/pathoptions.hxx>
#include <sfx2/filedlghelper.hxx>
#include <comphelper/processfactory.hxx>
+#include <svtools/linkpolicy.hxx>
#include <com/sun/star/media/XManager.hpp>
#include "com/sun/star/ui/dialogs/TemplateDescription.hpp"
@@ -525,10 +526,18 @@ uno::Reference< graphic::XGraphic >
MediaWindow::grabFrame( const ::rtl::OUStrin
bool
bAllowToCreateReplacementGraphic,
double fMediaTime )
{
- uno::Reference< media::XPlayer > xPlayer( createPlayer( rURL ) );
+ uno::Reference< media::XPlayer > xPlayer;
uno::Reference< graphic::XGraphic > xRet;
::std::auto_ptr< Graphic > apGraphic;
+ // Stricter than mayLoadDocumentReference: a grabbed frame is drawn into
the
+ // document and exported with it, so any reference that carries a scheme is
+ // put to the document, a local file included. A relative reference is the
+ // document's own content and is grabbed as before.
+ if( !::svt::linkpolicy::isAbsoluteUrl( rURL )
+ || ::svt::linkpolicy::mayFollowDocumentLink( rURL ) )
+ xPlayer = createPlayer( rURL );
+
if( xPlayer.is() )
{
uno::Reference< media::XFrameGrabber > xGrabber(
xPlayer->createFrameGrabber() );
diff --git a/main/avmedia/source/viewer/mediawindowbase_impl.cxx
b/main/avmedia/source/viewer/mediawindowbase_impl.cxx
index 94611576b5..8df4145924 100644
--- a/main/avmedia/source/viewer/mediawindowbase_impl.cxx
+++ b/main/avmedia/source/viewer/mediawindowbase_impl.cxx
@@ -29,6 +29,7 @@
#include <comphelper/processfactory.hxx>
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
#include <com/sun/star/media/XManager.hpp>
+#include <svtools/linkpolicy.hxx>
#ifndef _COM_SUN_STAR_LANG_XCOMPONENT_HDL_
#include <com/sun/star/lang/XComponent.hdl>
#endif
@@ -74,6 +75,12 @@ uno::Reference< media::XPlayer >
MediaWindowBaseImpl::createPlayer( const ::rtl:
rbJavaBased = sal_False;
+ // Every player in the office is built here, over a URL that comes out of
+ // document content, so the shared policy decides first (see
+ // svtools/linkpolicy.hxx).
+ if( !::svt::linkpolicy::mayLoadDocumentReference( rURL ) )
+ return xPlayer;
+
if( xContext.is() )
{
static const ServiceManager aServiceManagers[] =
diff --git a/main/editeng/source/items/frmitems.cxx
b/main/editeng/source/items/frmitems.cxx
index 78dfb27a0a..a7b9c9fa59 100644
--- a/main/editeng/source/items/frmitems.cxx
+++ b/main/editeng/source/items/frmitems.cxx
@@ -53,6 +53,7 @@
#include <limits.h>
#include <comphelper/processfactory.hxx>
#include <svtools/grfmgr.hxx>
+#include <svtools/linkpolicy.hxx>
#include <tools/urlobj.hxx>
#include <comphelper/types.hxx>
#include <svl/memberid.hrc>
@@ -4092,6 +4093,15 @@ const GraphicObject* SvxBrushItem::GetGraphicObject()
const
pImpl->aDoneLink = aTmp;
} */
+ // The link is document content, so the shared policy
decides (see
+ // svtools/linkpolicy.hxx). A refusal is final for this
item: no stream is
+ // opened, and no retry.
+ if( !::svt::linkpolicy::mayLoadDocumentReference(
*pStrLink ) )
+ {
+ const_cast < SvxBrushItem*> (this)->bLoadAgain
= sal_False;
+ return pImpl->pGraphicObject;
+ }
+
pImpl->pStream = utl::UcbStreamHelper::CreateStream(
*pStrLink, STREAM_STD_READ );
if( pImpl->pStream && !pImpl->pStream->GetError() )
{
diff --git a/main/forms/source/xforms/model.cxx
b/main/forms/source/xforms/model.cxx
index 2cda345230..2c4d386409 100644
--- a/main/forms/source/xforms/model.cxx
+++ b/main/forms/source/xforms/model.cxx
@@ -42,6 +42,7 @@
#include <comphelper/propertysetinfo.hxx>
#include <cppuhelper/typeprovider.hxx>
+#include <svtools/linkpolicy.hxx>
#include <algorithm>
@@ -452,6 +453,14 @@ void Model::loadInstance( sal_Int32 nInstance )
// if we have a URL, load the document and set it into the instance
if( sURL.getLength() > 0 )
{
+ // The instance src is document content, so the shared policy decides
(see
+ // svtools/linkpolicy.hxx).
+ if( !::svt::linkpolicy::mayLoadDocumentReference( sURL ) )
+ {
+ // Leave the instance empty, as for a src that cannot be read.
+ return;
+ }
+
try
{
Reference<XInputStream> xInput =
diff --git a/main/sfx2/source/appl/fileobj.cxx
b/main/sfx2/source/appl/fileobj.cxx
index 0b9e765b73..e0c4c7e8d7 100644
--- a/main/sfx2/source/appl/fileobj.cxx
+++ b/main/sfx2/source/appl/fileobj.cxx
@@ -28,6 +28,7 @@
#include <tools/stream.hxx>
#include <sot/formats.hxx>
#include <svtools/filter.hxx>
+#include <svtools/linkpolicy.hxx>
#include <sfx2/lnkbase.hxx>
#include <sfx2/app.hxx>
#include <sfx2/progress.hxx>
@@ -285,6 +286,15 @@ sal_Bool SvFileObject::LoadFile_Impl()
if( bWaitForData || !bLoadAgain || xMed.Is() || pDownLoadData )
return sal_False;
+ // The name is document content, so the shared policy decides before the
+ // fetch (see svtools/linkpolicy.hxx). The question is the LinkManager's
+ // existing one, asked at most once per document.
+ if( !::svt::linkpolicy::mayLoadDocumentReference( sFileNm ) )
+ {
+ bLoadAgain = sal_False;
+ return sal_False;
+ }
+
// z.Z. nur auf die aktuelle DocShell
xMed = new SfxMedium( sFileNm, STREAM_STD_READ, sal_True );
SvLinkSource::StreamToLoadFrom aStreamToLoadFrom =
@@ -328,6 +338,11 @@ sal_Bool SvFileObject::LoadFile_Impl()
sal_Bool SvFileObject::GetGraphic_Impl( Graphic& rGrf, SvStream* pStream )
{
+ // A second way out: with no stream and no medium the filter opens
sFileNm
+ // itself. Ask here too; the answer is remembered per document.
+ if( !::svt::linkpolicy::mayLoadDocumentReference( sFileNm ) )
+ return sal_False;
+
GraphicFilter* pGF = GraphicFilter::GetGraphicFilter();
const sal_uInt16 nFilter = sFilter.Len() && pGF->GetImportFormatCount()
diff --git a/main/sfx2/source/appl/linkmgr2.cxx
b/main/sfx2/source/appl/linkmgr2.cxx
index 2302d7f972..7811724c35 100644
--- a/main/sfx2/source/appl/linkmgr2.cxx
+++ b/main/sfx2/source/appl/linkmgr2.cxx
@@ -41,6 +41,7 @@
#include <svl/eitem.hxx>
#include <svl/intitem.hxx>
#include <unotools/localfilehelper.hxx>
+#include <svtools/linkpolicy.hxx>
#include <comphelper/processfactory.hxx>
#include <i18npool/mslangid.hxx>
#include <sfx2/request.hxx>
@@ -577,13 +578,9 @@ sal_Bool LinkManager::urlIsSafe( const
::com::sun::star::util::URL &url )
sal_Bool LinkManager::urlIsVendor( const ::rtl::OUString &url )
{
- if ( url.matchIgnoreAsciiCaseAsciiL( "vnd.sun.star.", 13, 0 ) ) {
- return url.matchIgnoreAsciiCaseAsciiL ( "expand", 6, 13 ) ||
- url.matchIgnoreAsciiCaseAsciiL ( "script", 6, 13 ) ||
- url.matchIgnoreAsciiCaseAsciiL ( "tdoc", 4, 13 ) ||
- url.matchIgnoreAsciiCaseAsciiL ( "uno", 3, 13 );
- }
- return sal_False;
+ // One definition of this scheme set, shared with the modules below sfx2
+ // that also have to apply it (see svtools/linkpolicy.hxx).
+ return ::svt::linkpolicy::isVendorUrl( url ) ? sal_True : sal_False;
}
diff --git a/main/svgio/source/svgreader/linkedimagepolicy.hxx
b/main/svgio/source/svgreader/linkedimagepolicy.hxx
new file mode 100644
index 0000000000..3ee4887e8c
--- /dev/null
+++ b/main/svgio/source/svgreader/linkedimagepolicy.hxx
@@ -0,0 +1,98 @@
+/**************************************************************
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *************************************************************/
+
+#ifndef INCLUDED_SVGIO_LINKEDIMAGEPOLICY_HXX
+#define INCLUDED_SVGIO_LINKEDIMAGEPOLICY_HXX
+
+#include "sal/config.h"
+
+#include "rtl/uri.hxx"
+#include "rtl/ustring.hxx"
+#include "svtools/linkpolicy.hxx"
+
+namespace svgio { namespace svgreader {
+
+/** Resolves an <image> reference against the SVG's own location and decides
+ whether it may be opened.
+
+ An <image> href is document content, read while the SVG is decomposed. The
+ reference is only followed when it stays with the SVG: the feature it
+ exists for is an image delivered alongside the file (a relative href), so
+ a target that resolves to some other place on the machine, to another
+ host, or into a different scheme is not part of that feature and is
+ refused.
+
+ Nothing here needs a document or a service: the decision is made from the
+ two strings, so it holds the same way on every route that reaches the
+ decompose step, and for a base URL that is itself remote (a remote base
+ contains no local files, so every local target falls outside it).
+
+ @param rBase
+ the SVG's own absolute location, as the document reports it.
+
+ @param rHref
+ the reference as it was written in the file, relative or absolute.
+
+ @param rAbsoluteUrl
+ on success, the resolved URL to open; untouched otherwise.
+
+ @return true when the reference may be opened.
+ */
+inline bool resolveLinkedImageUrl(
+ ::rtl::OUString const & rBase,
+ ::rtl::OUString const & rHref,
+ ::rtl::OUString & rAbsoluteUrl )
+{
+ if ( rBase.getLength() == 0 || rHref.getLength() == 0 )
+ return false;
+
+ // A malformed base makes the reference unresolvable. convertRelToAbs
+ // signals that by throwing, and the throw must not leave the decompose
+ // call -- it would cross a UNO interface that does not declare it.
+ ::rtl::OUString aAbsUrl;
+ try
+ {
+ aAbsUrl = ::rtl::Uri::convertRelToAbs( rBase, rHref );
+ }
+ catch ( const ::rtl::MalformedUriException & )
+ {
+ return false;
+ }
+ if ( aAbsUrl.getLength() == 0 )
+ return false;
+
+ // This is opened as a plain file, so a file: reference on this machine is
+ // the only thing that can be read here; the test also refuses the schemes
+ // that address process state and a UNC reference to another host.
+ if ( !::svt::linkpolicy::isLocalFileUrl( aAbsUrl ) )
+ return false;
+
+ // ... and only where the SVG itself sits.
+ if ( !::svt::linkpolicy::isContainedUnder( rBase, aAbsUrl ) )
+ return false;
+
+ rAbsoluteUrl = aAbsUrl;
+ return true;
+}
+
+} }
+
+#endif
diff --git a/main/svgio/source/svgreader/svgimagenode.cxx
b/main/svgio/source/svgreader/svgimagenode.cxx
index 4014766e06..18fbeba63b 100644
--- a/main/svgio/source/svgreader/svgimagenode.cxx
+++ b/main/svgio/source/svgreader/svgimagenode.cxx
@@ -36,6 +36,7 @@
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <rtl/uri.hxx>
+#include "linkedimagepolicy.hxx"
#include <drawinglayer/geometry/viewinformation2d.hxx>
//////////////////////////////////////////////////////////////////////////////
@@ -244,9 +245,12 @@ namespace svgio
if(rPath.getLength())
{
- const rtl::OUString
aAbsUrl(rtl::Uri::convertRelToAbs(rPath, maUrl));
+ // The reference is document content: resolve it
+ // against this file's own location and only open
it
+ // when it stays there (see linkedimagepolicy.hxx).
+ rtl::OUString aAbsUrl;
- if(aAbsUrl.getLength())
+ if(resolveLinkedImageUrl(rPath, maUrl, aAbsUrl))
{
SvFileStream aStream(aAbsUrl, STREAM_STD_READ);
Graphic aGraphic;
diff --git a/main/svtools/Library_svt.mk b/main/svtools/Library_svt.mk
index aae315f1fd..9864002d26 100644
--- a/main/svtools/Library_svt.mk
+++ b/main/svtools/Library_svt.mk
@@ -218,6 +218,7 @@ $(eval $(call gb_Library_add_exception_objects,svt,\
svtools/source/misc/imap3 \
svtools/source/misc/itemdel \
svtools/source/misc/langtab \
+ svtools/source/misc/linkpolicy \
svtools/source/misc/stringtransfer \
svtools/source/misc/svtaccessiblefactory \
svtools/source/misc/svtdata \
diff --git a/main/svtools/Package_inc.mk b/main/svtools/Package_inc.mk
index 9bb8dcceaa..829a5bfd09 100644
--- a/main/svtools/Package_inc.mk
+++ b/main/svtools/Package_inc.mk
@@ -99,6 +99,7 @@ $(eval $(call
gb_Package_add_file,svtools_inc,inc/svtools/ivctrl.hxx,svtools/ivc
$(eval $(call
gb_Package_add_file,svtools_inc,inc/svtools/javacontext.hxx,svtools/javacontext.hxx))
$(eval $(call
gb_Package_add_file,svtools_inc,inc/svtools/javainteractionhandler.hxx,svtools/javainteractionhandler.hxx))
$(eval $(call
gb_Package_add_file,svtools_inc,inc/svtools/langtab.hxx,svtools/langtab.hxx))
+$(eval $(call
gb_Package_add_file,svtools_inc,inc/svtools/linkpolicy.hxx,svtools/linkpolicy.hxx))
$(eval $(call
gb_Package_add_file,svtools_inc,inc/svtools/localresaccess.hxx,svtools/localresaccess.hxx))
$(eval $(call
gb_Package_add_file,svtools_inc,inc/svtools/menuoptions.hxx,svtools/menuoptions.hxx))
$(eval $(call
gb_Package_add_file,svtools_inc,inc/svtools/miscopt.hxx,svtools/miscopt.hxx))
diff --git a/main/svtools/inc/svtools/linkpolicy.hxx
b/main/svtools/inc/svtools/linkpolicy.hxx
new file mode 100644
index 0000000000..473a09cdf3
--- /dev/null
+++ b/main/svtools/inc/svtools/linkpolicy.hxx
@@ -0,0 +1,251 @@
+/**************************************************************
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *************************************************************/
+
+#ifndef INCLUDED_SVTOOLS_LINKPOLICY_HXX
+#define INCLUDED_SVTOOLS_LINKPOLICY_HXX
+
+#include "sal/config.h"
+
+#include "rtl/string.h"
+#include "rtl/uri.hxx"
+#include "rtl/ustring.hxx"
+#include "svtools/svtdllapi.h"
+
+/** Policy for following a URL that came out of document content.
+
+ Document content names resources by URL -- linked graphics, background
+ images, instance data, media. These helpers are the one place that decides
+ what may be followed, so the modules that hold the actual load calls
+ (editeng, svgio, forms, sw, svtools itself) all apply the same rule instead
+ of each carrying its own test.
+
+ The classifying functions are inline and depend on nothing but rtl, so they
+ can be exercised directly. mayFollowDocumentLink() needs the running
+ document and therefore lives in the library.
+ */
+namespace svt { namespace linkpolicy {
+
+/** Tells whether a URL uses one of the schemes that address process state or
+ an internal object tree rather than an ordinary resource.
+
+ vnd.sun.star.expand resolves bootstrap macros; script, tdoc and uno reach
+ the scripting engine, the loaded document tree and the object model. None
+ of them is a meaningful target for document content, so these are never
+ followed regardless of any other permission.
+ */
+inline bool isVendorUrl( ::rtl::OUString const & rUrl )
+{
+ if ( !rUrl.matchIgnoreAsciiCaseAsciiL(
+ RTL_CONSTASCII_STRINGPARAM( "vnd.sun.star." ) ) )
+ return false;
+ sal_Int32 const n = RTL_CONSTASCII_LENGTH( "vnd.sun.star." );
+ return rUrl.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM(
"expand" ), n )
+ || rUrl.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM(
"script" ), n )
+ || rUrl.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "tdoc"
), n )
+ || rUrl.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "uno"
), n );
+}
+
+/** Tells whether a reference carries a scheme, i.e. names a resource by
+ itself rather than relative to the document that holds it.
+
+ A scheme is a name followed by ':' before any path separator. Callers use
+ this to leave a document-relative reference alone: relative references
+ stay inside the document's own area and are the ordinary case, while a
+ scheme is what lets a reference point somewhere else entirely.
+ */
+inline bool isAbsoluteUrl( ::rtl::OUString const & rUrl )
+{
+ sal_Int32 const nColon = rUrl.indexOf( ':' );
+ if ( nColon <= 0 )
+ return false;
+ sal_Int32 const nSlash = rUrl.indexOf( '/' );
+ if ( nSlash != -1 && nSlash < nColon )
+ return false;
+ // A scheme starts with a letter (RFC 3986); a Windows drive letter
+ // followed by ':' is one character and cannot be confused with it.
+ sal_Unicode const c = rUrl.getStr()[0];
+ if ( !( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) ) )
+ return false;
+ return nColon > 1;
+}
+
+/** Tells whether a URL is a file: reference whose authority names a host
+ other than this one.
+
+ On Windows such a reference resolves to a UNC path, so the access is a
+ request to another machine rather than a read of a local file. An empty
+ authority and localhost are this machine and are not affected.
+ */
+inline bool isOffHostFileUrl( ::rtl::OUString const & rUrl )
+{
+ if ( !rUrl.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "file:"
) ) )
+ return false;
+ ::rtl::OUString aRest( rUrl.copy( RTL_CONSTASCII_LENGTH( "file:" ) ) );
+
+ // A backslash is not URL syntax; on Windows it would form a UNC
+ // reference once the URL is converted to a system path.
+ if ( aRest.indexOf( '\\' ) != -1 )
+ return true;
+
+ if ( !aRest.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "//" )
) )
+ return false; // no authority component at all
+
+ sal_Int32 const nEnd = aRest.indexOf( '/', 2 );
+ ::rtl::OUString const aAuthority(
+ nEnd == -1 ? aRest.copy( 2 ) : aRest.copy( 2, nEnd - 2 ) );
+ if ( aAuthority.getLength() != 0
+ && !aAuthority.equalsIgnoreAsciiCaseAsciiL(
+ RTL_CONSTASCII_STRINGPARAM( "localhost" ) ) )
+ return true;
+
+ // An empty authority followed by a path that starts another one
+ // (file:////host/share) names a different host just the same.
+ ::rtl::OUString const aPath(
+ nEnd == -1 ? ::rtl::OUString() : aRest.copy( nEnd ) );
+ return aPath.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "//"
) );
+}
+
+/** Tells whether a URL names a file on this machine: the file: scheme, an
+ absolute path, and no authority pointing elsewhere.
+ */
+inline bool isLocalFileUrl( ::rtl::OUString const & rUrl )
+{
+ if ( !rUrl.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "file:"
) ) )
+ return false;
+ if ( isOffHostFileUrl( rUrl ) )
+ return false;
+ ::rtl::OUString aRest( rUrl.copy( RTL_CONSTASCII_LENGTH( "file:" ) ) );
+ if ( aRest.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM( "//" )
) )
+ {
+ sal_Int32 const nEnd = aRest.indexOf( '/', 2 );
+ if ( nEnd == -1 )
+ return false; // authority only, no path
+ aRest = aRest.copy( nEnd );
+ }
+ return aRest.getLength() > 1 && aRest.getStr()[0] == '/';
+}
+
+/** Tells whether rTarget stays inside the directory rBase sits in.
+
+ Both are expected to be absolute URLs, rTarget already resolved against
+ rBase. Comparing the whole string means the scheme and the authority have
+ to agree as well, so a target on another host or in another scheme is not
+ contained. A segment that would climb back out is refused whether it is
+ spelled plainly or percent-encoded, because the escape only disappears
+ when the URL is turned into a system path.
+
+ This is the test for the case where a document may reference a file
+ delivered alongside it, but nothing else.
+ */
+inline bool isContainedUnder(
+ ::rtl::OUString const & rBase, ::rtl::OUString const & rTarget )
+{
+ if ( rBase.getLength() == 0 || rTarget.getLength() == 0 )
+ return false;
+
+ // The directory rBase lives in, trailing separator included.
+ sal_Int32 const nSlash = rBase.lastIndexOf( '/' );
+ if ( nSlash == -1 )
+ return false;
+ ::rtl::OUString const aDir( rBase.copy( 0, nSlash + 1 ) );
+
+ if ( !rTarget.match( aDir ) )
+ return false;
+ ::rtl::OUString aRest( rTarget.copy( aDir.getLength() ) );
+ if ( aRest.getLength() == 0 )
+ return false; // the directory itself
+ if ( aRest.getStr()[0] == '/' )
+ return false; // would open an authority
+
+ // Refuse a climbing segment in either spelling. Decoding can throw on
+ // malformed escapes; that is a refusal too.
+ ::rtl::OUString aDecoded;
+ try
+ {
+ aDecoded = ::rtl::Uri::decode(
+ aRest, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
+ }
+ catch ( ... )
+ {
+ return false;
+ }
+ if ( aDecoded.getLength() == 0 )
+ return false;
+ if ( aDecoded.indexOf( '\\' ) != -1 )
+ return false;
+
+ for ( sal_Int32 i = 0; i != -1; )
+ {
+ ::rtl::OUString const aSegment( aDecoded.getToken( 0, '/', i ) );
+ if ( aSegment.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ".." ) ) )
+ return false;
+ }
+ return true;
+}
+
+/** Decides whether document content may follow rUrl.
+
+ A vendor URL (see isVendorUrl) is never followed. Otherwise the running
+ document decides, through com::sun::star::document::XLinkAuthorizer, which
+ refuses the request, permits it, or asks the user -- the same decision the
+ linked-graphic loader has been making, now shared. When no document can be
+ reached the URL is followed, as that loader has always done; callers that
+ can enforce something stricter on their own (a containment rule, a scheme
+ restriction) should apply it in addition to this.
+ */
+SVT_DLLPUBLIC bool mayFollowDocumentLink( ::rtl::OUString const & rUrl );
+
+/** Decides whether a reference out of document content may be loaded.
+
+ This is the form a load site wants unless it has a reason for something
+ else. A relative reference stays in the document's own area, and a file on
+ this machine is what a document has always been able to name, so both are
+ loaded as before -- no new question is put to the user for the ordinary
+ case. Anything else either leaves this machine (a remote scheme, or a
+ file: URL whose authority names another host) or addresses process state,
+ and needs permission first.
+
+ A load site that can be stricter should be: GraphicObject asks about every
+ URL, including local ones, and keeps doing so.
+ */
+inline bool mayLoadDocumentReference( ::rtl::OUString const & rUrl )
+{
+ if ( rUrl.getLength() == 0 )
+ return false;
+ if ( !isAbsoluteUrl( rUrl ) )
+ {
+ // No scheme, so relative to the document -- except for a system path
that
+ // opens with a host name, which names another machine just the same. A
+ // caller that does not convert to a URL first hands such a path on
+ // unchanged.
+ if ( rUrl.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM(
"\\\\" ) )
+ || rUrl.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM(
"//" ) ) )
+ return mayFollowDocumentLink( rUrl );
+ return true;
+ }
+ if ( isLocalFileUrl( rUrl ) )
+ return true;
+ return mayFollowDocumentLink( rUrl );
+}
+
+} }
+
+#endif
diff --git a/main/svtools/source/graphic/grfmgr.cxx
b/main/svtools/source/graphic/grfmgr.cxx
index ccf81764c7..cfe7959727 100644
--- a/main/svtools/source/graphic/grfmgr.cxx
+++ b/main/svtools/source/graphic/grfmgr.cxx
@@ -44,6 +44,7 @@
#include <vcl/salbtype.hxx>
#include <unotools/cacheoptions.hxx>
#include <svtools/grfmgr.hxx>
+#include <svtools/linkpolicy.hxx>
// --> OD 2010-01-04 #i105243#
#include <vcl/pdfextoutdevdata.hxx>
@@ -1447,32 +1448,10 @@ GraphicObject
GraphicObject::CreateGraphicObjectFromURL( const ::rtl::OUString &
Graphic aGraphic;
if ( aURL.Len() )
{
- /* We must obtain authorization from the current
document, and we
- need a ServiceManager to access it. Because
utl::UcbStreamHelper
- relies on the ::ucbhelper::ContentBroker instance,
we will
- use its ServiceManager. */
- ::ucbhelper::ContentBroker* pBroker =
::ucbhelper::ContentBroker::get();
- if ( pBroker ) {
- uno::Reference< lang::XMultiServiceFactory >
xFactory = pBroker->getServiceManager();
- uno::Any desktop( xFactory->createInstance(
::rtl::OUString::createFromAscii( "com.sun.star.frame.Desktop" ) ) );
- uno::Reference< com::sun::star::frame::XDesktop
> xDesktop( desktop, uno::UNO_QUERY );
- if ( xDesktop.is() ) {
- uno::Reference<
::com::sun::star::frame::XFrame > xFrame = xDesktop->getCurrentFrame();
- if ( xFrame.is() ) {
- uno::Reference<
::com::sun::star::frame::XController > xController = xFrame->getController();
- if ( xController.is() ) {
- uno::Reference<
::com::sun::star::frame::XModel > xModel = xController->getModel();
- if ( xModel.is() ) {
- uno::Reference<
com::sun::star::document::XLinkAuthorizer > xLinkAuthorizer( xModel,
uno::UNO_QUERY);
- if (
xLinkAuthorizer.is() ) {
- if (
!xLinkAuthorizer->authorizeLinks( aURL ) )
-
return GraphicObject( aGraphic );
- }
- }
- }
- }
- }
- }
+ // The URL comes out of document content, so it is
followed only if
+ // the shared policy permits it (see
svtools/linkpolicy.hxx).
+ if ( !::svt::linkpolicy::mayFollowDocumentLink( rURL ) )
+ return GraphicObject( aGraphic );
SvStream* pStream =
utl::UcbStreamHelper::CreateStream( aURL, STREAM_READ );
if( pStream )
diff --git a/main/svtools/source/misc/linkpolicy.cxx
b/main/svtools/source/misc/linkpolicy.cxx
new file mode 100644
index 0000000000..02a848875a
--- /dev/null
+++ b/main/svtools/source/misc/linkpolicy.cxx
@@ -0,0 +1,91 @@
+/**************************************************************
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_svtools.hxx"
+
+#include <com/sun/star/document/XLinkAuthorizer.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/XDesktop.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <ucbhelper/contentbroker.hxx>
+
+#include <svtools/linkpolicy.hxx>
+
+using namespace ::com::sun::star;
+
+namespace svt { namespace linkpolicy {
+
+bool mayFollowDocumentLink( ::rtl::OUString const & rUrl )
+{
+ if ( rUrl.getLength() == 0 )
+ return false;
+
+ // These schemes reach process state or the object model; no permission
+ // makes them a sensible target for document content.
+ if ( isVendorUrl( rUrl ) )
+ return false;
+
+ /* Ask the current document, which needs a ServiceManager to reach.
+ Because utl::UcbStreamHelper relies on the ::ucbhelper::ContentBroker
+ instance, use its ServiceManager. */
+ ::ucbhelper::ContentBroker* pBroker = ::ucbhelper::ContentBroker::get();
+ if ( pBroker )
+ {
+ uno::Reference< lang::XMultiServiceFactory > xFactory =
+ pBroker->getServiceManager();
+ if ( xFactory.is() )
+ {
+ uno::Any desktop( xFactory->createInstance(
+ ::rtl::OUString::createFromAscii( "com.sun.star.frame.Desktop"
) ) );
+ uno::Reference< frame::XDesktop > xDesktop( desktop,
uno::UNO_QUERY );
+ if ( xDesktop.is() )
+ {
+ uno::Reference< frame::XFrame > xFrame(
xDesktop->getCurrentFrame() );
+ if ( xFrame.is() )
+ {
+ uno::Reference< frame::XController > xController(
+ xFrame->getController() );
+ if ( xController.is() )
+ {
+ uno::Reference< frame::XModel > xModel(
+ xController->getModel() );
+ if ( xModel.is() )
+ {
+ uno::Reference< document::XLinkAuthorizer >
+ xLinkAuthorizer( xModel, uno::UNO_QUERY );
+ if ( xLinkAuthorizer.is() )
+ return xLinkAuthorizer->authorizeLinks( rUrl );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // No document to ask: behave as the linked-graphic loader always has.
+ return true;
+}
+
+} }
diff --git a/main/sw/source/core/graphic/ndgrf.cxx
b/main/sw/source/core/graphic/ndgrf.cxx
index 05dd8e6050..61936e769a 100644
--- a/main/sw/source/core/graphic/ndgrf.cxx
+++ b/main/sw/source/core/graphic/ndgrf.cxx
@@ -30,6 +30,7 @@
#endif
#include <svtools/imap.hxx>
#include <svtools/filter.hxx>
+#include <svtools/linkpolicy.hxx>
#include <sot/storage.hxx>
#include <sfx2/linkmgr.hxx>
#include <editeng/boxitem.hxx>
@@ -161,11 +162,19 @@ SwGrfNode::SwGrfNode(
if ( IsLinkedFile() )
{
INetURLObject aUrl( rGrfName );
- if ( INET_PROT_FILE == aUrl.GetProtocol() &&
- FStatHelper::IsDocument( aUrl.GetMainURL(
INetURLObject::NO_DECODE ) ) )
+ if ( INET_PROT_FILE == aUrl.GetProtocol() )
{
- // File vorhanden, Verbindung herstellen ohne ein Update
- ( (SwBaseLink*) &refLink )->Connect();
+ const String aMainUrl( aUrl.GetMainURL( INetURLObject::NO_DECODE )
);
+
+ // The name is document content, so the shared policy decides
before the stat
+ // (see svtools/linkpolicy.hxx): where the authority names another
host, the
+ // stat is itself a request to that host.
+ if ( ::svt::linkpolicy::mayLoadDocumentReference( aMainUrl ) &&
+ FStatHelper::IsDocument( aMainUrl ) )
+ {
+ // File vorhanden, Verbindung herstellen ohne ein Update
+ ( (SwBaseLink*) &refLink )->Connect();
+ }
}
}
}
diff --git a/main/sw/source/core/layout/paintfrm.cxx
b/main/sw/source/core/layout/paintfrm.cxx
index 8a54bcadf4..08f25ed32d 100644
--- a/main/sw/source/core/layout/paintfrm.cxx
+++ b/main/sw/source/core/layout/paintfrm.cxx
@@ -1610,6 +1610,14 @@ void lcl_DrawGraphic( const SvxBrushItem& rBrush,
OutputDevice *pOut,
//Hier kein Link, wir wollen die Grafik synchron laden!
((SvxBrushItem&)rBrush).SetDoneLink( Link() );
GraphicObject *pGrf = (GraphicObject*)rBrush.GetGraphicObject();
+ if ( !pGrf )
+ {
+ // The graphic is missing, or loading it was not permitted;
there is
+ // nothing to draw and nothing to draw a background behind.
+ if ( bNotInside )
+ pOut->Pop();
+ return;
+ }
/// OD 17.10.2002 #103876# - outsourcing drawing of background with a
background color.
::lcl_DrawGraphicBackgrd( rBrush, pOut, aAlignedGrfRect, *pGrf, bGrfNum,
bBackgrdAlreadyDrawn );
@@ -1829,6 +1837,14 @@ void MA_FASTCALL DrawGraphic(
// before drawing tiled graphic in loop
// determine graphic object
GraphicObject* pGraphicObj = const_cast< GraphicObject*
>(pBrush->GetGraphicObject());
+ if ( !pGraphicObj )
+ {
+ // Nothing to tile: the graphic is missing or was not permitted
+ // to load. Leave the background colour to the code below, as
+ // for a brush with no graphic at all.
+ bDraw = sal_False;
+ break;
+ }
// calculate aligned paint rectangle
SwRect aAlignedPaintRect = rOut;
::SwAlignRect( aAlignedPaintRect, &rSh );
@@ -1918,21 +1934,27 @@ void MA_FASTCALL DrawGraphic(
(ePos != GPOS_TILED) && (ePos != GPOS_AREA)
)
{
+ // A brush can name a graphic that is not there to load -- the file
+ // is missing, or loading it was not permitted -- in which case
+ // there is nothing to ask about its transparency.
GraphicObject *pGrf = (GraphicObject*)pBrush->GetGraphicObject();
- if ( bConsiderBackgroundTransparency )
+ if ( pGrf )
{
- GraphicAttr pGrfAttr = pGrf->GetAttr();
- if ( (pGrfAttr.GetTransparency() != 0) &&
- ( pBrush && (pBrush->GetColor() == COL_TRANSPARENT) )
- )
+ if ( bConsiderBackgroundTransparency )
{
- bTransparentGrfWithNoFillBackgrd = true;
- nGrfTransparency = pGrfAttr.GetTransparency();
+ GraphicAttr pGrfAttr = pGrf->GetAttr();
+ if ( (pGrfAttr.GetTransparency() != 0) &&
+ ( pBrush && (pBrush->GetColor() == COL_TRANSPARENT) )
+ )
+ {
+ bTransparentGrfWithNoFillBackgrd = true;
+ nGrfTransparency = pGrfAttr.GetTransparency();
+ }
+ }
+ if ( pGrf->IsTransparent() )
+ {
+ bGrfIsTransparent = true;
}
- }
- if ( pGrf->IsTransparent() )
- {
- bGrfIsTransparent = true;
}
}