Python script auto-execution changes:
authorCampbell Barton <ideasman42@gmail.com>
Mon, 10 Jun 2013 00:42:16 +0000 (00:42 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Mon, 10 Jun 2013 00:42:16 +0000 (00:42 +0000)
- script execution is off by default

- if a blend file attempts to execute a script
  this shows a message in the header with the action
  that was suppressed (script/driver/game-autostart) and 2 buttons to either reload the file trusted, or to ignore the message.

- the file selector will always default to use the trust setting in the user preferences,
  but reloading an open file will keep using the current setting (whatever was set before or set on the command-line).

- added SCons setting WITH_BF_PYTHON_SECURITY, this sets the default state for the user prefereces not to trust blend files on load.
  ... this option was in CMake before, but always off, now its enabled by default for SCons and CMake, and forced on in CMake for now.

14 files changed:
CMakeLists.txt
build_files/scons/tools/btools.py
release/scripts/startup/bl_ui/space_info.py
source/blender/blenkernel/BKE_global.h
source/blender/editors/space_script/script_edit.c
source/blender/editors/space_script/script_intern.h
source/blender/editors/space_script/script_ops.c
source/blender/python/intern/bpy_app.c
source/blender/python/intern/bpy_driver.c
source/blender/python/intern/bpy_interface.c
source/blender/windowmanager/SConscript
source/blender/windowmanager/intern/wm_files.c
source/blender/windowmanager/intern/wm_operators.c
source/creator/creator.c

index 86848d10422a1ad6b75d0416417c1104a47864b2..39b75d9a32b46d95af316474542b41c859ac05b5 100644 (file)
@@ -118,9 +118,10 @@ mark_as_advanced(WITH_BLENDER)
 option(WITH_INTERNATIONAL "Enable I18N (International fonts and text)" ON)
 
 option(WITH_PYTHON        "Enable Embedded Python API  (only disable for development)" ON)
-option(WITH_PYTHON_SECURITY "Disables execution of scripts within blend files by default (recommend to leave off)" OFF)
+option(WITH_PYTHON_SECURITY "Disables execution of scripts within blend files by default" ON) 
 mark_as_advanced(WITH_PYTHON)  # dont want people disabling this unless they really know what they are doing.
 mark_as_advanced(WITH_PYTHON_SECURITY)  # some distributions see this as a security issue, rather than have them patch it, make a build option.
+set(WITH_PYTHON_SECURITY ON CACHE BOOL "ON" FORCE) # temp force on. 
 
 option(WITH_PYTHON_SAFETY "Enable internal API error checking to track invalid data to prevent crash on access (at the expense of some effeciency, only enable for development)." OFF)
 option(WITH_PYTHON_MODULE "Enable building as a python module which runs without a user interface, like running regular blender in background mode (experimental, only enable for development)" OFF)
index b8d036d9a8413fefd253952930a5b5054d9ae2bb..d28521a8d078e2b10cef0977eff8c547dd7e196c 100644 (file)
@@ -96,7 +96,7 @@ def print_arguments(args, bc):
 
 def validate_arguments(args, bc):
     opts_list = [
-            'WITH_BF_FREESTYLE', 'WITH_BF_PYTHON', 'WITH_BF_PYTHON_SAFETY', 'BF_PYTHON', 'BF_PYTHON_VERSION', 'BF_PYTHON_INC', 'BF_PYTHON_BINARY', 'BF_PYTHON_LIB', 'BF_PYTHON_LIBPATH', 'BF_PYTHON_LIBPATH_ARCH', 'WITH_BF_STATICPYTHON', 'WITH_OSX_STATICPYTHON', 'BF_PYTHON_LIB_STATIC', 'BF_PYTHON_DLL', 'BF_PYTHON_ABI_FLAGS',
+            'WITH_BF_FREESTYLE', 'WITH_BF_PYTHON', 'WITH_BF_PYTHON_SAFETY', 'WITH_BF_PYTHON_SECURITY', 'BF_PYTHON', 'BF_PYTHON_VERSION', 'BF_PYTHON_INC', 'BF_PYTHON_BINARY', 'BF_PYTHON_LIB', 'BF_PYTHON_LIBPATH', 'BF_PYTHON_LIBPATH_ARCH', 'WITH_BF_STATICPYTHON', 'WITH_OSX_STATICPYTHON', 'BF_PYTHON_LIB_STATIC', 'BF_PYTHON_DLL', 'BF_PYTHON_ABI_FLAGS',
             'WITH_BF_OPENAL', 'BF_OPENAL', 'BF_OPENAL_INC', 'BF_OPENAL_LIB', 'BF_OPENAL_LIBPATH', 'WITH_BF_STATICOPENAL', 'BF_OPENAL_LIB_STATIC',
             'WITH_BF_SDL', 'BF_SDL', 'BF_SDL_INC', 'BF_SDL_LIB', 'BF_SDL_LIBPATH',
             'WITH_BF_JACK', 'BF_JACK', 'BF_JACK_INC', 'BF_JACK_LIB', 'BF_JACK_LIBPATH', 'WITH_BF_JACK_DYNLOAD',
@@ -254,6 +254,7 @@ def read_opts(env, cfg, args):
         ('LIBDIR', 'Root dir of libs'),
         (BoolVariable('WITH_BF_PYTHON', 'Compile with python', True)),
         (BoolVariable('WITH_BF_PYTHON_SAFETY', 'Internal API error checking to track invalid data to prevent crash on access (at the expense of some effeciency)', False)),
+        (BoolVariable('WITH_BF_PYTHON_SECURITY', 'Disables execution of scripts within blend files by default', True)),
         ('BF_PYTHON', 'Base path for python', ''),
         ('BF_PYTHON_VERSION', 'Python version to use', ''),
         ('BF_PYTHON_INC', 'Include path for Python headers', ''),
index 4ea366e35418148b7f275768e03059c5d033afb5..1885f115aa83ea87a0c13f10b63a0583b93f4b9d 100644 (file)
@@ -64,6 +64,17 @@ class INFO_HT_header(Header):
         layout.template_reports_banner()
 
         row = layout.row(align=True)
+
+        if bpy.app.autoexec_fail is True and bpy.app.autoexec_fail_quiet is False:
+            layout.operator_context = 'EXEC_DEFAULT'
+            row.label("Script failed to auto-run", icon='ERROR')
+            props = row.operator("wm.open_mainfile", icon='SCREEN_BACK', text="Reload Trusted")
+            props.filepath = bpy.data.filepath
+            props.use_scripts = True
+            row.operator("script.autoexec_warn_clear", icon='CANCEL')
+            row.label("Skipping: (%s)" % bpy.app.autoexec_fail_message)
+            return
+
         row.operator("wm.splash", text="", icon='BLENDER', emboss=False)
         row.label(text=scene.statistics(), translate=False)
 
index a0c6bcd4a5bb43c865eb904cd598ed0a818b0358..8a55d3e8a176ec1cfd2a83e729062378003f9cbe 100644 (file)
@@ -93,6 +93,9 @@ typedef struct Global {
 
        /* save the allowed windowstate of blender when using -W or -w (GHOST_TWindowState) */
        int windowstate;
+
+       /* message to use when autoexec fails */
+       char autoexec_fail[200];
 } Global;
 
 /* **************** GLOBAL ********************* */
@@ -109,6 +112,8 @@ typedef struct Global {
 
 #define G_SCRIPT_AUTOEXEC (1 << 13)
 #define G_SCRIPT_OVERRIDE_PREF (1 << 14) /* when this flag is set ignore the userprefs */
+#define G_SCRIPT_AUTOEXEC_FAIL (1 << 15)
+#define G_SCRIPT_AUTOEXEC_FAIL_QUIET (1 << 16)
 
 /* #define G_NOFROZEN  (1 << 17) also removed */
 /* #define G_GREASEPENCIL   (1 << 17)   also removed */
index 96008004ee447aa11f4796df4c4bd4c07e06679f..8074ec57474a28acc63bddb9e7e430b52ad27db3 100644 (file)
@@ -36,6 +36,7 @@
 #include "BLI_utildefines.h"
 
 #include "BKE_context.h"
+#include "BKE_global.h"
 
 #include "WM_api.h"
 #include "WM_types.h"
@@ -110,3 +111,23 @@ void SCRIPT_OT_reload(wmOperatorType *ot)
        /* api callbacks */
        ot->exec = script_reload_exec;
 }
+
+static int script_autoexec_warn_clear_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
+{
+       G.f |= G_SCRIPT_AUTOEXEC_FAIL_QUIET;
+       return OPERATOR_FINISHED;
+}
+
+void SCRIPT_OT_autoexec_warn_clear(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Continue Untrusted";
+       ot->description = "Ignore autoexec warning";
+       ot->idname = "SCRIPT_OT_autoexec_warn_clear";
+
+       /* flags */
+       ot->flag = OPTYPE_INTERNAL;
+
+       /* api callbacks */
+       ot->exec = script_autoexec_warn_clear_exec;
+}
index 008638639943076b3d899be53b67768c22135ca0..cef6082aa1c1ac9c5a9e48d4edfebcdb0192f46f 100644 (file)
@@ -40,6 +40,7 @@ void script_keymap(struct wmKeyConfig *keyconf);
 /* script_edit.c */
 void SCRIPT_OT_reload(struct wmOperatorType *ot);
 void SCRIPT_OT_python_file_run(struct wmOperatorType *ot);
+void SCRIPT_OT_autoexec_warn_clear(struct wmOperatorType *ot);
 
 #endif /* __SCRIPT_INTERN_H__ */
 
index 57a1112a83249147e8f3654360ee7bdd24a6492d..7398126a8e569d5e7f6f4adb989629e2156a1b39 100644 (file)
@@ -56,6 +56,7 @@ void script_operatortypes(void)
 {
        WM_operatortype_append(SCRIPT_OT_python_file_run);
        WM_operatortype_append(SCRIPT_OT_reload);
+       WM_operatortype_append(SCRIPT_OT_autoexec_warn_clear);
 }
 
 void script_keymap(wmKeyConfig *keyconf)
index 17dafc4ac52ac522724492e092323e6647bb5b1e..2edb0aee783139b675ceb938d1c34f92dd2df8e7 100644 (file)
@@ -219,6 +219,12 @@ static int bpy_app_debug_value_set(PyObject *UNUSED(self), PyObject *value, void
        return 0;
 }
 
+static PyObject *bpy_app_global_flag_get(PyObject *UNUSED(self), void *closure)
+{
+       const int flag = GET_INT_FROM_POINTER(closure);
+       return PyBool_FromLong(G.f & flag);
+}
+
 PyDoc_STRVAR(bpy_app_tempdir_doc,
 "String, the temp directory used by blender (read-only)"
 );
@@ -243,6 +249,11 @@ static PyObject *bpy_app_driver_dict_get(PyObject *UNUSED(self), void *UNUSED(cl
        return bpy_pydriver_Dict;
 }
 
+static PyObject *bpy_app_autoexec_fail_message_get(PyObject *UNUSED(self), void *UNUSED(closure))
+{
+       return PyC_UnicodeFromByte(G.autoexec_fail);
+}
+
 
 static PyGetSetDef bpy_app_getsets[] = {
        {(char *)"debug",           bpy_app_debug_get, bpy_app_debug_set, (char *)bpy_app_debug_doc, (void *)G_DEBUG},
@@ -256,6 +267,11 @@ static PyGetSetDef bpy_app_getsets[] = {
        {(char *)"debug_value", bpy_app_debug_value_get, bpy_app_debug_value_set, (char *)bpy_app_debug_value_doc, NULL},
        {(char *)"tempdir", bpy_app_tempdir_get, NULL, (char *)bpy_app_tempdir_doc, NULL},
        {(char *)"driver_namespace", bpy_app_driver_dict_get, NULL, (char *)bpy_app_driver_dict_doc, NULL},
+
+       /* security */
+       {(char *)"autoexec_fail", bpy_app_global_flag_get, NULL, NULL, (void *)G_SCRIPT_AUTOEXEC_FAIL},
+       {(char *)"autoexec_fail_quiet", bpy_app_global_flag_get, NULL, NULL, (void *)G_SCRIPT_AUTOEXEC_FAIL_QUIET},
+       {(char *)"autoexec_fail_message", bpy_app_autoexec_fail_message_get, NULL, NULL, NULL},
        {NULL, NULL, NULL, NULL, NULL}
 };
 
index 72bfda89b649d4ad46e7a3e6a85150fc2d1732e7..e7c0b7b88115b585c611cb30b2b28468f75a3d5f 100644 (file)
@@ -37,6 +37,7 @@
 
 #include "BLI_listbase.h"
 #include "BLI_math_base.h"
+#include "BLI_string.h"
 
 #include "BKE_fcurve.h"
 #include "BKE_global.h"
@@ -189,7 +190,12 @@ float BPY_driver_exec(ChannelDriver *driver, const float evaltime)
                return 0.0f;
 
        if (!(G.f & G_SCRIPT_AUTOEXEC)) {
-               printf("skipping driver '%s', automatic scripts are disabled\n", driver->expression);
+               if (!(G.f & G_SCRIPT_AUTOEXEC_FAIL_QUIET)) {
+                       G.f |= G_SCRIPT_AUTOEXEC_FAIL;
+                       BLI_snprintf(G.autoexec_fail, sizeof(G.autoexec_fail), "Driver '%s'", driver->expression);
+
+                       printf("skipping driver '%s', automatic scripts are disabled\n", driver->expression);
+               }
                return 0.0f;
        }
 
index 75827ce8bbfe24d62c62d69fdf197331347d3ad3..c39168779fd1f6f8dfc311cf3f2a989b10b558ac 100644 (file)
@@ -398,6 +398,10 @@ void BPY_python_end(void)
 
 void BPY_python_reset(bContext *C)
 {
+       /* unrelated security stuff */
+       G.f &= ~(G_SCRIPT_AUTOEXEC_FAIL | G_SCRIPT_AUTOEXEC_FAIL_QUIET);
+       G.autoexec_fail[0] = '\0';
+
        BPY_driver_reset();
        BPY_app_handlers_reset(false);
        BPY_modules_load_user(C);
@@ -725,7 +729,12 @@ void BPY_modules_load_user(bContext *C)
        for (text = bmain->text.first; text; text = text->id.next) {
                if (text->flags & TXT_ISSCRIPT && BLI_testextensie(text->id.name + 2, ".py")) {
                        if (!(G.f & G_SCRIPT_AUTOEXEC)) {
-                               printf("scripts disabled for \"%s\", skipping '%s'\n", bmain->name, text->id.name + 2);
+                               if (!(G.f & G_SCRIPT_AUTOEXEC_FAIL_QUIET)) {
+                                       G.f |= G_SCRIPT_AUTOEXEC_FAIL;
+                                       BLI_snprintf(G.autoexec_fail, sizeof(G.autoexec_fail), "Register Text '%s'", text->id.name + 2);
+
+                                       printf("scripts disabled for \"%s\", skipping '%s'\n", bmain->name, text->id.name + 2);
+                               }
                        }
                        else {
                                PyObject *module = bpy_text_import(text);
index 3a813d7d5985c3d9edb568c7787d639e745f00ac..0a2f48f048808e9e432232805bdf32159c8799fe 100644 (file)
@@ -78,4 +78,7 @@ if env['WITH_BF_INTERNATIONAL']:
 if env['WITH_BF_COMPOSITOR']:
     defs.append("WITH_COMPOSITOR")
 
+if env['WITH_BF_PYTHON_SECURITY']:
+    defs.append("WITH_PYTHON_SECURITY")
+
 env.BlenderLib ( 'bf_windowmanager', sources, Split(incs), defines=defs, libtype=['core'], priority=[5] )
index c372e2d1ce62ce32eee9f54005bb2970917c2675..f9e931180338a476e1d4591f4bf4cd07b488f458 100644 (file)
@@ -541,7 +541,7 @@ int wm_homefile_read(bContext *C, ReportList *UNUSED(reports), short from_memory
                success = BKE_read_file_from_memory(C, datatoc_startup_blend, datatoc_startup_blend_size, NULL);
                if (wmbase.first == NULL) wm_clear_default_size(C);
 
-#ifdef WITH_PYTHON_SECURITY /* not default */
+#ifdef WITH_PYTHON_SECURITY
                /* use alternative setting for security nuts
                 * otherwise we'd need to patch the binary blob - startup.blend.c */
                U.flag |= USER_SCRIPT_AUTOEXEC_DISABLE;
index 423ddfcc568dc1d518e190480899157a942ca6d9..d832e84d7f05dbb3be3eddbad8491c608da56e7f 100644 (file)
@@ -1916,19 +1916,26 @@ static void WM_OT_read_factory_settings(wmOperatorType *ot)
 
 /* *************** open file **************** */
 
-static void open_set_load_ui(wmOperator *op)
+static void open_set_load_ui(wmOperator *op, bool use_prefs)
 {
-       if (!RNA_struct_property_is_set(op->ptr, "load_ui"))
-               RNA_boolean_set(op->ptr, "load_ui", !(U.flag & USER_FILENOUI));
+       PropertyRNA *prop = RNA_struct_find_property(op->ptr, "load_ui");
+       if (!RNA_property_is_set(op->ptr, prop)) {
+               RNA_property_boolean_set(op->ptr, prop, use_prefs ?
+                                        (U.flag & USER_FILENOUI) == 0 :
+                                        (G.fileflags |= G_FILE_NO_UI) == 0);
+       }
 }
 
-static void open_set_use_scripts(wmOperator *op)
+static void open_set_use_scripts(wmOperator *op, bool use_prefs)
 {
-       if (!RNA_struct_property_is_set(op->ptr, "use_scripts")) {
+       PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts");
+       if (!RNA_property_is_set(op->ptr, prop)) {
                /* use G_SCRIPT_AUTOEXEC rather than the userpref because this means if
                 * the flag has been disabled from the command line, then opening
                 * from the menu wont enable this setting. */
-               RNA_boolean_set(op->ptr, "use_scripts", (G.f & G_SCRIPT_AUTOEXEC));
+               RNA_property_boolean_set(op->ptr, prop, use_prefs ?
+                                        (U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0 :
+                                        (G.f & G_SCRIPT_AUTOEXEC) != 0);
        }
 }
 
@@ -1951,8 +1958,8 @@ static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *U
        }
 
        RNA_string_set(op->ptr, "filepath", openname);
-       open_set_load_ui(op);
-       open_set_use_scripts(op);
+       open_set_load_ui(op, true);
+       open_set_use_scripts(op, true);
 
        WM_event_add_fileselect(C, op);
 
@@ -1964,8 +1971,10 @@ static int wm_open_mainfile_exec(bContext *C, wmOperator *op)
        char path[FILE_MAX];
 
        RNA_string_get(op->ptr, "filepath", path);
-       open_set_load_ui(op);
-       open_set_use_scripts(op);
+
+       /* re-use last loaded setting so we can reload a file without changing */
+       open_set_load_ui(op, false);
+       open_set_use_scripts(op, false);
 
        if (RNA_boolean_get(op->ptr, "load_ui"))
                G.fileflags &= ~G_FILE_NO_UI;
index 1451a52569435582b5bb409e04b0d88c5ffa01d9..302467c6b3d5e57bcfbd9500615e3c06bb280d86 100644 (file)
@@ -1639,11 +1639,21 @@ int main(int argc, const char **argv)
                WM_exit(C);
        }
        else {
-               if ((G.fileflags & G_FILE_AUTOPLAY) && (G.f & G_SCRIPT_AUTOEXEC)) {
-                       if (WM_init_game(C))
-                               return 0;
+               if (G.fileflags & G_FILE_AUTOPLAY) {
+                       if (G.f & G_SCRIPT_AUTOEXEC) {
+                               if (WM_init_game(C)) {
+                                       return 0;
+                               }
+                       }
+                       else {
+                               if (!(G.f & G_SCRIPT_AUTOEXEC_FAIL_QUIET)) {
+                                       G.f |= G_SCRIPT_AUTOEXEC_FAIL;
+                                       BLI_snprintf(G.autoexec_fail, sizeof(G.autoexec_fail), "Game AutoStart");
+                               }
+                       }
                }
-               else if (!G.file_loaded) {
+
+               if (!G.file_loaded) {
                        WM_init_splash(C);
                }
        }