tdf#171924 Make the run macro dialog check security like organizer

When a macro is chosen from the Run Macro dialog, the script context is
now taken from the model that the script is contained in instead of the
current frame. If the script isn’t embedded in a document then it won’t
have a model so the security check will pass. This makes the Run Macro
dialog work like the run button in the Organize Macros dialog. It also
means the dialog can be used from the start center.

Change-Id: Ibe4a1632ca8d26307296f30a8041b90aff86bc30
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/206153
Tested-by: Jenkins
Reviewed-by: Neil Roberts <nroberts@libreoffice.org>
This commit is contained in:
Neil Roberts
2026-06-09 11:46:46 +02:00
parent e34d43d53c
commit 45caec45f6
2 changed files with 124 additions and 11 deletions
+122
View File
@@ -0,0 +1,122 @@
# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
#
# 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/.
#
from uitest.framework import UITestCase
from uitest.uihelper.common import get_state_as_dict
from libreoffice.uno.propertyvalue import mkPropertyValues
from com.sun.star.beans import PropertyValue
from com.sun.star.script.provider import ScriptURIHelper
from com.sun.star.ucb import SimpleFileAccess
import uno
import tempfile
import os
def get_user_script_directory(context):
uriHelper = ScriptURIHelper.create(context, "Python", "user")
uri = uriHelper.getRootStorageURI()
return uno.fileUrlToSystemPath(uri)
def generate_test_script_for_directory(directory):
# Generate a python script that creates a file in the given directory
return f"""
import os.path
def MyMacro():
with open(os.path.join(r'{directory}', 'script-ran'), 'x'):
pass
"""
class RunMacroTest(UITestCase):
def run_macro(self, *parts):
with self.ui_test.execute_dialog_through_command(".uno:RunMacro") as xDialog:
xNode = xDialog.getChild("categories")
for i, part in enumerate(parts):
if i > 0:
xNode.executeAction("EXPAND", tuple())
# Find the node in the tree with this part name
xNode = \
next(x for x in map(lambda i: xNode.getChild(i),
range(int(get_state_as_dict(xNode)["Children"])))
if get_state_as_dict(x)["Text"] == part)
xNode.executeAction("SELECT", tuple())
def run_user_script(self):
script_dir = get_user_script_directory(self.xContext)
os.makedirs(script_dir, exist_ok=True)
# Create a temporary directory in the script directory to contain our test script
with tempfile.TemporaryDirectory(dir=script_dir) as temp_script_dir:
# Create a temporary directory to create the check file in
with tempfile.TemporaryDirectory() as check_file_dir:
# Create a script with a known name
with open(os.path.join(temp_script_dir, "MyScript.py"), "w", encoding="utf-8") as f:
print(generate_test_script_for_directory(check_file_dir), file=f)
self.run_macro("My Macros", os.path.basename(temp_script_dir), "MyScript")
# Return whether the test file was created
return os.path.exists(os.path.join(check_file_dir, 'script-ran'))
def test_start_center(self):
# Try running a script directly from the start center. See tdf#171924
self.assertTrue(self.run_user_script())
def test_embedded_script(self):
# Create a temporary directory to create the check file in
with tempfile.TemporaryDirectory() as check_file_dir:
# Create a new document with a temporary filename and add a test script to it
with tempfile.NamedTemporaryFile(suffix=".odt") as doc_file:
with self.ui_test.create_doc_in_start_center("writer") as xComponent:
xFileAccess = SimpleFileAccess.create(self.xContext)
xFileAccess.createFolder("vnd.sun.star.tdoc:/1/Scripts/python")
with tempfile.NamedTemporaryFile(suffix=".py", mode="w+",
encoding="utf-8") as script_file:
print(generate_test_script_for_directory(check_file_dir),
file=script_file)
script_file.flush()
xFileAccess.copy(uno.systemPathToFileUrl(script_file.name),
"vnd.sun.star.tdoc:/1/Scripts/python/MyScript.py")
# Save the document to the temporary file
save_opts = (
PropertyValue(Name="Overwrite", Value=True),
PropertyValue(Name="FilerName", Value="writer8"),
)
xComponent.storeAsURL(uno.systemPathToFileUrl(doc_file.name), save_opts)
# Open the document we just created
with self.ui_test.load_file(uno.systemPathToFileUrl(doc_file.name)):
# We should be able to run user macros even though we’re looking at a document
# with macros in it
self.assertTrue(self.run_user_script())
# We shouldn’t be able to run the embedded script because the security policy
# disallows it
self.run_macro(os.path.basename(doc_file.name), "MyScript")
self.assertFalse(os.path.exists(os.path.join(check_file_dir, 'script-ran')))
# Temporarily disable macro security
with self.ui_test.set_config("/org.openoffice.Office.Common/Security/Scripting/"
"MacroSecurityLevel", 0):
with self.ui_test.load_file(uno.systemPathToFileUrl(doc_file.name)):
# Now we should be able to run the embedded script
self.run_macro(os.path.basename(doc_file.name), "MyScript")
self.assertTrue(os.path.exists(os.path.join(check_file_dir, 'script-ran')))
# vim: set shiftwidth=4 softtabstop=4 expandtab:
+2 -11
View File
@@ -1848,17 +1848,8 @@ void SfxApplication::OfaExec_Impl( SfxRequest& rReq )
Sequence< Any > outArgs;
Any ret;
Reference< XInterface > xScriptContext;
Reference< XController > xController;
if ( xFrame.is() )
xController = xFrame->getController();
if ( xController.is() )
xScriptContext = xController->getModel();
if ( !xScriptContext.is() )
xScriptContext = xController;
SfxObjectShell::CallXScript( xScriptContext, pDlg->GetScriptURL(), args, ret, outIndex, outArgs );
SfxObjectShell::CallXScript( pDlg->GetScriptModel(), pDlg->GetScriptURL(), args,
ret, outIndex, outArgs );
pDlg->disposeOnce();
});
}