diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index ce676a8639..3828eafafb 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -3203,6 +3203,7 @@ static void expand_by_function(int type, char *base, Callback *cb) assert(curbuf != NULL); const bool is_cpt_function = (cb != NULL); + const bool use_sandbox = is_cpt_function && was_set_insecurely(curwin, kOptComplete, OPT_LOCAL); if (!is_cpt_function) { char *funcname = get_complete_funcname(type); if (*funcname == NUL) { @@ -3224,9 +3225,16 @@ static void expand_by_function(int type, char *base, Callback *cb) // switching to another window, it should not be needed and may end up in // Insert mode in another buffer. textlock++; + if (use_sandbox) { + sandbox++; + } // Call a function, which returns a list or dict. - if (callback_call(cb, 2, args, &rettv)) { + const bool called = callback_call(cb, 2, args, &rettv); + if (use_sandbox) { + sandbox--; + } + if (called) { switch (rettv.v_type) { case VAR_LIST: matchlist = rettv.vval.v_list; @@ -5796,6 +5804,7 @@ static int get_userdefined_compl_info(colnr_T curs_col, Callback *cb, int *start const int save_State = State; const bool is_cpt_function = (cb != NULL); + const bool use_sandbox = is_cpt_function && was_set_insecurely(curwin, kOptComplete, OPT_LOCAL); if (!is_cpt_function) { // Call 'completefunc' or 'omnifunc' or 'thesaurusfunc' and get pattern // length as a string @@ -5816,7 +5825,13 @@ static int get_userdefined_compl_info(colnr_T curs_col, Callback *cb, int *start pos_T pos = curwin->w_cursor; textlock++; + if (use_sandbox) { + sandbox++; + } colnr_T col = (colnr_T)callback_call_retnr(cb, 2, args); + if (use_sandbox) { + sandbox--; + } textlock--; State = save_State; diff --git a/test/old/testdir/test_modeline.vim b/test/old/testdir/test_modeline.vim index 6ffcf4c717..3c6dd6bc85 100644 --- a/test/old/testdir/test_modeline.vim +++ b/test/old/testdir/test_modeline.vim @@ -289,6 +289,59 @@ func Test_modeline_fails_modelineexpr() call s:modeline_fails('titlestring', 'titlestring=Something()', 'E992:') endfunc +func Test_modeline_complete_uses_sandbox() + let modeline = &modeline + let modelineexpr = &modelineexpr + + func! ModelineCompletePwnFindstart(findstart, base) + if a:findstart + call writefile(['findstart'], 'Xmodeline_complete_proof') + return 0 + endif + return ['match'] + endfunc + + func! ModelineCompletePwnMatches(findstart, base) + if a:findstart + return 0 + endif + call writefile(['matches'], 'Xmodeline_complete_proof') + return ['match'] + endfunc + + try + set modeline modelineexpr + + call writefile([ + \ 'vim: set complete=FModelineCompletePwnFindstart :', + \ 'body', + \ ], 'Xmodeline_complete_attack', 'D') + call delete('Xmodeline_complete_proof') + edit Xmodeline_complete_attack + call cursor(2, 1) + call assert_fails('call feedkeys("i\\", "xt")', 'E48:') + call assert_false(filereadable('Xmodeline_complete_proof')) + bwipe! + + call writefile([ + \ 'vim: set complete=FModelineCompletePwnMatches :', + \ 'body', + \ ], 'Xmodeline_complete_attack', 'D') + call delete('Xmodeline_complete_proof') + edit Xmodeline_complete_attack + call cursor(2, 1) + call assert_fails('call feedkeys("i\\", "xt")', 'E48:') + call assert_false(filereadable('Xmodeline_complete_proof')) + bwipe! + finally + let &modeline = modeline + let &modelineexpr = modelineexpr + call delete('Xmodeline_complete_proof') + delfunc ModelineCompletePwnFindstart + delfunc ModelineCompletePwnMatches + endtry +endfunc + func Test_modeline_setoption_verbose() let modeline = &modeline set modeline