mirror of
https://github.com/neovim/neovim.git
synced 2025-10-03 08:28:34 +00:00
vim-patch:9.1.1802: 'nowrap' in a modeline may hide malicious code (#35946)
Problem: 'nowrap' in a modeline may hide malicious code.
Solution: Forcibly use '>' as 'listchars' "extends" if 'nowrap' was set
from a modeline (zeertzjq).
Manual `:setlocal nowrap` disables this behavior. There is a separate
problem with `:set nowrap` that also applies to some other options.
related: vim/vim#18214
related: vim/vim#18399
closes: vim/vim#18425
9d5208a931
Cherry-pick some test_modeline.vim changes from patches 9.0.{0363,0626}.
This commit is contained in:
@@ -7639,6 +7639,11 @@ A jump table for the options with a short description can be found at |Q_op|.
|
|||||||
< See 'sidescroll', 'listchars' and |wrap-off|.
|
< See 'sidescroll', 'listchars' and |wrap-off|.
|
||||||
This option can't be set from a |modeline| when the 'diff' option is
|
This option can't be set from a |modeline| when the 'diff' option is
|
||||||
on.
|
on.
|
||||||
|
If 'nowrap' was set from a |modeline| or in the |sandbox|, '>' is used
|
||||||
|
as the |lcs-extends| character regardless of the value of the 'list'
|
||||||
|
and 'listchars' options. This is to prevent malicious code outside
|
||||||
|
the viewport from going unnoticed. Use `:setlocal nowrap` manually
|
||||||
|
afterwards to disable this behavior.
|
||||||
|
|
||||||
*'wrapmargin'* *'wm'*
|
*'wrapmargin'* *'wm'*
|
||||||
'wrapmargin' 'wm' number (default 0)
|
'wrapmargin' 'wm' number (default 0)
|
||||||
|
5
runtime/lua/vim/_meta/options.lua
generated
5
runtime/lua/vim/_meta/options.lua
generated
@@ -8408,6 +8408,11 @@ vim.go.wiw = vim.go.winwidth
|
|||||||
--- See 'sidescroll', 'listchars' and `wrap-off`.
|
--- See 'sidescroll', 'listchars' and `wrap-off`.
|
||||||
--- This option can't be set from a `modeline` when the 'diff' option is
|
--- This option can't be set from a `modeline` when the 'diff' option is
|
||||||
--- on.
|
--- on.
|
||||||
|
--- If 'nowrap' was set from a `modeline` or in the `sandbox`, '>' is used
|
||||||
|
--- as the `lcs-extends` character regardless of the value of the 'list'
|
||||||
|
--- and 'listchars' options. This is to prevent malicious code outside
|
||||||
|
--- the viewport from going unnoticed. Use `:setlocal nowrap` manually
|
||||||
|
--- afterwards to disable this behavior.
|
||||||
---
|
---
|
||||||
--- @type boolean
|
--- @type boolean
|
||||||
vim.o.wrap = true
|
vim.o.wrap = true
|
||||||
|
@@ -1303,6 +1303,7 @@ struct window_S {
|
|||||||
#define GLOBAL_WO(p) ((char *)(p) + sizeof(winopt_T))
|
#define GLOBAL_WO(p) ((char *)(p) + sizeof(winopt_T))
|
||||||
|
|
||||||
// A few options have local flags for kOptFlagInsecure.
|
// A few options have local flags for kOptFlagInsecure.
|
||||||
|
uint32_t w_p_wrap_flags; // flags for 'wrap'
|
||||||
uint32_t w_p_stl_flags; // flags for 'statusline'
|
uint32_t w_p_stl_flags; // flags for 'statusline'
|
||||||
uint32_t w_p_wbr_flags; // flags for 'winbar'
|
uint32_t w_p_wbr_flags; // flags for 'winbar'
|
||||||
uint32_t w_p_fde_flags; // flags for 'foldexpr'
|
uint32_t w_p_fde_flags; // flags for 'foldexpr'
|
||||||
|
@@ -153,6 +153,21 @@ void drawline_free_all_mem(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// Get the 'listchars' "extends" characters to use for "wp", or NUL if it
|
||||||
|
/// shouldn't be used.
|
||||||
|
static schar_T get_lcs_ext(win_T *wp)
|
||||||
|
{
|
||||||
|
if (wp->w_p_wrap) {
|
||||||
|
// Line never continues beyond the right of the screen with 'wrap'.
|
||||||
|
return NUL;
|
||||||
|
}
|
||||||
|
if (wp->w_p_wrap_flags & kOptFlagInsecure) {
|
||||||
|
// If 'nowrap' was set from a modeline, forcibly use '>'.
|
||||||
|
return schar_from_ascii('>');
|
||||||
|
}
|
||||||
|
return wp->w_p_list ? wp->w_p_lcs_chars.ext : NUL;
|
||||||
|
}
|
||||||
|
|
||||||
/// Advance wlv->color_cols if not NULL
|
/// Advance wlv->color_cols if not NULL
|
||||||
static void advance_color_col(winlinevars_T *wlv, int vcol)
|
static void advance_color_col(winlinevars_T *wlv, int vcol)
|
||||||
{
|
{
|
||||||
@@ -2865,12 +2880,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show "extends" character from 'listchars' if beyond the line end and
|
// Show "extends" character from 'listchars' if beyond the line end.
|
||||||
// 'list' is set.
|
const schar_T lcs_ext = get_lcs_ext(wp);
|
||||||
// Don't show this with 'wrap' as the line can't be scrolled horizontally.
|
if (lcs_ext != NUL
|
||||||
if (wp->w_p_lcs_chars.ext != NUL
|
|
||||||
&& wp->w_p_list
|
|
||||||
&& !wp->w_p_wrap
|
|
||||||
&& wlv.filler_todo <= 0
|
&& wlv.filler_todo <= 0
|
||||||
&& wlv.col == view_width - 1
|
&& wlv.col == view_width - 1
|
||||||
&& !has_foldtext) {
|
&& !has_foldtext) {
|
||||||
@@ -2882,7 +2894,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
|||||||
|| (lcs_eol > 0 && lcs_eol_todo)
|
|| (lcs_eol > 0 && lcs_eol_todo)
|
||||||
|| (wlv.n_extra > 0 && (wlv.sc_extra != NUL || *wlv.p_extra != NUL))
|
|| (wlv.n_extra > 0 && (wlv.sc_extra != NUL || *wlv.p_extra != NUL))
|
||||||
|| (may_have_inline_virt && has_more_inline_virt(&wlv, ptr - line))) {
|
|| (may_have_inline_virt && has_more_inline_virt(&wlv, ptr - line))) {
|
||||||
mb_schar = wp->w_p_lcs_chars.ext;
|
mb_schar = lcs_ext;
|
||||||
wlv.char_attr = win_hl_attr(wp, HLF_AT);
|
wlv.char_attr = win_hl_attr(wp, HLF_AT);
|
||||||
mb_c = schar_get_first_codepoint(mb_schar);
|
mb_c = schar_get_first_codepoint(mb_schar);
|
||||||
}
|
}
|
||||||
|
@@ -1644,6 +1644,8 @@ uint32_t *insecure_flag(win_T *const wp, OptIndex opt_idx, int opt_flags)
|
|||||||
if (opt_flags & OPT_LOCAL) {
|
if (opt_flags & OPT_LOCAL) {
|
||||||
assert(wp != NULL);
|
assert(wp != NULL);
|
||||||
switch (opt_idx) {
|
switch (opt_idx) {
|
||||||
|
case kOptWrap:
|
||||||
|
return &wp->w_p_wrap_flags;
|
||||||
case kOptStatusline:
|
case kOptStatusline:
|
||||||
return &wp->w_p_stl_flags;
|
return &wp->w_p_stl_flags;
|
||||||
case kOptWinbar:
|
case kOptWinbar:
|
||||||
|
@@ -10811,6 +10811,11 @@ local options = {
|
|||||||
< See 'sidescroll', 'listchars' and |wrap-off|.
|
< See 'sidescroll', 'listchars' and |wrap-off|.
|
||||||
This option can't be set from a |modeline| when the 'diff' option is
|
This option can't be set from a |modeline| when the 'diff' option is
|
||||||
on.
|
on.
|
||||||
|
If 'nowrap' was set from a |modeline| or in the |sandbox|, '>' is used
|
||||||
|
as the |lcs-extends| character regardless of the value of the 'list'
|
||||||
|
and 'listchars' options. This is to prevent malicious code outside
|
||||||
|
the viewport from going unnoticed. Use `:setlocal nowrap` manually
|
||||||
|
afterwards to disable this behavior.
|
||||||
]=],
|
]=],
|
||||||
full_name = 'wrap',
|
full_name = 'wrap',
|
||||||
redraw = { 'current_window' },
|
redraw = { 'current_window' },
|
||||||
|
@@ -358,22 +358,69 @@ endfunc
|
|||||||
|
|
||||||
" Some options cannot be set from the modeline when 'diff' option is set
|
" Some options cannot be set from the modeline when 'diff' option is set
|
||||||
func Test_modeline_diff_buffer()
|
func Test_modeline_diff_buffer()
|
||||||
call writefile(['vim: diff foldmethod=marker wrap'], 'Xfile')
|
call writefile(['vim: diff foldmethod=marker wrap'], 'Xmdifile', 'D')
|
||||||
set foldmethod& nowrap
|
set foldmethod& nowrap
|
||||||
new Xfile
|
new Xmdifile
|
||||||
call assert_equal('manual', &foldmethod)
|
call assert_equal('manual', &foldmethod)
|
||||||
call assert_false(&wrap)
|
call assert_false(&wrap)
|
||||||
set wrap&
|
set wrap&
|
||||||
call delete('Xfile')
|
|
||||||
bw
|
bw
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_modeline_disable()
|
func Test_modeline_disable()
|
||||||
set modeline
|
set modeline
|
||||||
call writefile(['vim: sw=2', 'vim: nomodeline', 'vim: sw=3'], 'Xmodeline_disable')
|
call writefile(['vim: sw=2', 'vim: nomodeline', 'vim: sw=3'], 'Xmodeline_disable', 'D')
|
||||||
edit Xmodeline_disable
|
edit Xmodeline_disable
|
||||||
call assert_equal(2, &sw)
|
call assert_equal(2, &sw)
|
||||||
call delete('Xmodeline_disable')
|
endfunc
|
||||||
|
|
||||||
|
" If 'nowrap' is set from a modeline, '>' is used forcibly as lcs-extends.
|
||||||
|
func Test_modeline_nowrap_lcs_extends()
|
||||||
|
call writefile([
|
||||||
|
\ 'aaa',
|
||||||
|
\ 'bbb',
|
||||||
|
\ 'ccc evil',
|
||||||
|
\ 'ddd vim: nowrap',
|
||||||
|
\ ], 'Xmodeline_nowrap', 'D')
|
||||||
|
call NewWindow(10, 20)
|
||||||
|
|
||||||
|
setlocal nolist listchars=
|
||||||
|
edit Xmodeline_nowrap
|
||||||
|
let expect_insecure = [
|
||||||
|
\ 'aaa ',
|
||||||
|
\ 'bbb ',
|
||||||
|
\ 'ccc >',
|
||||||
|
\ 'ddd >',
|
||||||
|
\ '~ ',
|
||||||
|
\ ]
|
||||||
|
call assert_equal(expect_insecure, ScreenLines([1, 5], 20))
|
||||||
|
|
||||||
|
setlocal nowrap
|
||||||
|
let expect_secure = [
|
||||||
|
\ 'aaa ',
|
||||||
|
\ 'bbb ',
|
||||||
|
\ 'ccc ',
|
||||||
|
\ 'ddd ',
|
||||||
|
\ '~ ',
|
||||||
|
\ ]
|
||||||
|
call assert_equal(expect_secure, ScreenLines([1, 5], 20))
|
||||||
|
|
||||||
|
setlocal list listchars=extends:+
|
||||||
|
let expect_secure = [
|
||||||
|
\ 'aaa ',
|
||||||
|
\ 'bbb ',
|
||||||
|
\ 'ccc +',
|
||||||
|
\ 'ddd +',
|
||||||
|
\ '~ ',
|
||||||
|
\ ]
|
||||||
|
call assert_equal(expect_secure, ScreenLines([1, 5], 20))
|
||||||
|
|
||||||
|
edit Xmodeline_nowrap
|
||||||
|
call assert_equal(expect_insecure, ScreenLines([1, 5], 20))
|
||||||
|
setlocal nowrap
|
||||||
|
call assert_equal(expect_secure, ScreenLines([1, 5], 20))
|
||||||
|
|
||||||
|
call CloseWindow()
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" vim: shiftwidth=2 sts=2 expandtab
|
" vim: shiftwidth=2 sts=2 expandtab
|
||||||
|
Reference in New Issue
Block a user