mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	fix(api): validate command names in nvim_add_user_command (#17406)
This uses the same validation used when defining commands with `:command`.
This commit is contained in:
		| @@ -1384,6 +1384,11 @@ void add_user_command(String name, Object command, Dict(user_command) *opts, int | |||||||
|   LuaRef luaref = LUA_NOREF; |   LuaRef luaref = LUA_NOREF; | ||||||
|   LuaRef compl_luaref = LUA_NOREF; |   LuaRef compl_luaref = LUA_NOREF; | ||||||
|  |  | ||||||
|  |   if (!uc_validate_name(name.data)) { | ||||||
|  |     api_set_error(err, kErrorTypeValidation, "Invalid command name"); | ||||||
|  |     goto err; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   if (mb_islower(name.data[0])) { |   if (mb_islower(name.data[0])) { | ||||||
|     api_set_error(err, kErrorTypeValidation, "'name' must begin with an uppercase letter"); |     api_set_error(err, kErrorTypeValidation, "'name' must begin with an uppercase letter"); | ||||||
|     goto err; |     goto err; | ||||||
|   | |||||||
| @@ -5164,6 +5164,24 @@ char_u *get_command_name(expand_T *xp, int idx) | |||||||
|   return cmdnames[idx].cmd_name; |   return cmdnames[idx].cmd_name; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /// Check for a valid user command name | ||||||
|  | /// | ||||||
|  | /// If the given {name} is valid, then a pointer to the end of the valid name is returned. | ||||||
|  | /// Otherwise, returns NULL. | ||||||
|  | char *uc_validate_name(char *name) | ||||||
|  | { | ||||||
|  |   if (ASCII_ISALPHA(*name)) { | ||||||
|  |     while (ASCII_ISALNUM(*name)) { | ||||||
|  |       name++; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if (!ends_excmd(*name) && !ascii_iswhite(*name)) { | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return name; | ||||||
|  | } | ||||||
|  |  | ||||||
| int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def, int flags, | int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def, int flags, | ||||||
|                    int compl, char_u *compl_arg, LuaRef compl_luaref, cmd_addr_T addr_type, |                    int compl, char_u *compl_arg, LuaRef compl_luaref, cmd_addr_T addr_type, | ||||||
|                    LuaRef luaref, bool force) |                    LuaRef luaref, bool force) | ||||||
| @@ -5679,23 +5697,18 @@ static void ex_command(exarg_T *eap) | |||||||
|  |  | ||||||
|   // Get the name (if any) and skip to the following argument. |   // Get the name (if any) and skip to the following argument. | ||||||
|   name = p; |   name = p; | ||||||
|   if (ASCII_ISALPHA(*p)) { |   end = (char_u *)uc_validate_name((char *)name); | ||||||
|     while (ASCII_ISALNUM(*p)) { |   if (!end) { | ||||||
|       p++; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   if (!ends_excmd(*p) && !ascii_iswhite(*p)) { |  | ||||||
|     emsg(_("E182: Invalid command name")); |     emsg(_("E182: Invalid command name")); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|   end = p; |   name_len = (size_t)(end - name); | ||||||
|   name_len = (int)(end - name); |  | ||||||
|  |  | ||||||
|   // If there is nothing after the name, and no attributes were specified, |   // If there is nothing after the name, and no attributes were specified, | ||||||
|   // we are listing commands |   // we are listing commands | ||||||
|   p = skipwhite(end); |   p = skipwhite(end); | ||||||
|   if (!has_attr && ends_excmd(*p)) { |   if (!has_attr && ends_excmd(*p)) { | ||||||
|     uc_list(name, end - name); |     uc_list(name, name_len); | ||||||
|   } else if (!ASCII_ISUPPER(*name)) { |   } else if (!ASCII_ISUPPER(*name)) { | ||||||
|     emsg(_("E183: User defined commands must start with an uppercase letter")); |     emsg(_("E183: User defined commands must start with an uppercase letter")); | ||||||
|   } else if (name_len <= 4 && STRNCMP(name, "Next", name_len) == 0) { |   } else if (name_len <= 4 && STRNCMP(name, "Next", name_len) == 0) { | ||||||
| @@ -5703,7 +5716,7 @@ static void ex_command(exarg_T *eap) | |||||||
|   } else if (compl > 0 && (argt & EX_EXTRA) == 0) { |   } else if (compl > 0 && (argt & EX_EXTRA) == 0) { | ||||||
|     emsg(_(e_complete_used_without_nargs)); |     emsg(_(e_complete_used_without_nargs)); | ||||||
|   } else { |   } else { | ||||||
|     uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, LUA_NOREF, |     uc_add_command(name, name_len, p, argt, def, flags, compl, compl_arg, LUA_NOREF, | ||||||
|                    addr_type_arg, LUA_NOREF, eap->forceit); |                    addr_type_arg, LUA_NOREF, eap->forceit); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -180,6 +180,28 @@ describe('nvim_add_user_command', function() | |||||||
|     feed('<C-U>Test b<Tab>') |     feed('<C-U>Test b<Tab>') | ||||||
|     eq('Test bbb', funcs.getcmdline()) |     eq('Test bbb', funcs.getcmdline()) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
|  |   it('does not allow invalid command names', function() | ||||||
|  |     matches("'name' must begin with an uppercase letter", pcall_err(exec_lua, [[ | ||||||
|  |       vim.api.nvim_add_user_command('test', 'echo "hi"', {}) | ||||||
|  |     ]])) | ||||||
|  |  | ||||||
|  |     matches('Invalid command name', pcall_err(exec_lua, [[ | ||||||
|  |       vim.api.nvim_add_user_command('t@', 'echo "hi"', {}) | ||||||
|  |     ]])) | ||||||
|  |  | ||||||
|  |     matches('Invalid command name', pcall_err(exec_lua, [[ | ||||||
|  |       vim.api.nvim_add_user_command('T@st', 'echo "hi"', {}) | ||||||
|  |     ]])) | ||||||
|  |  | ||||||
|  |     matches('Invalid command name', pcall_err(exec_lua, [[ | ||||||
|  |       vim.api.nvim_add_user_command('Test!', 'echo "hi"', {}) | ||||||
|  |     ]])) | ||||||
|  |  | ||||||
|  |     matches('Invalid command name', pcall_err(exec_lua, [[ | ||||||
|  |       vim.api.nvim_add_user_command('💩', 'echo "hi"', {}) | ||||||
|  |     ]])) | ||||||
|  |   end) | ||||||
| end) | end) | ||||||
|  |  | ||||||
| describe('nvim_del_user_command', function() | describe('nvim_del_user_command', function() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Gregory Anders
					Gregory Anders