Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
CI (node >= 14, Linux/macOS/Windows) | Coverage | npm |
---|---|---|
For "remote plugins", the Nvim Node.js provider expects the neovim
package to be globally installed:
npm install -g neovim
Or for non-plugin purposes, neovim
works like any other NPM package.
See below for a quickstart example that you can copy and run immediately.
The neovim
package provides these functions:
attach()
: The primary interface. Takes a process, socket, or pair of write/read streams and returns a NeovimClient
connected to an nvim
process.findNvim()
: Tries to find a usable nvim
binary on the current system.neovim
module replaces ("monkey patches") console
with its logger
interface, so console.log
will call logger.info
instead of writing to stdout (which would
break the stdio RPC channel).
console.log
, pass a custom logger
to attach()
.logger
available from the NeovimClient
returned by
attach()
, instead of console
logging functions.$NVIM_NODE_LOG_FILE
env var to (also) write logs to a file.$ALLOW_CONSOLE
env var to (also) write logs to stdout. This will break any (stdio) RPC
channel because logs written to stdout are invalid RPC messages.Following is a complete, working example.
neovim
package locally in any directory (i.e. without -g
. Node throws ERR_MODULE_NOT_FOUND
if a script imports a globally installed package).
npm install neovim
demo.mjs
file and run it!
ALLOW_CONSOLE=1 node demo.mjs
$ALLOW_CONSOLE
env var must be set, because logs are normally not printed to stdout.
$ALLOW_CONSOLE
is only for demo purposes. It cannot be used for remote plugins or
whenever stdio is an RPC channel, because writing logs to stdout would break the RPC
channel.import * as child_process from 'node:child_process'
import * as assert from 'node:assert'
import { attach, findNvim } from 'neovim'
// Find `nvim` on the system and open a channel to it.
(async function() {
const found = findNvim({ orderBy: 'desc', minVersion: '0.9.0' })
console.log(found);
const nvim_proc = child_process.spawn(found.matches[0].path, ['--clean', '--embed'], {});
const nvim = attach({ proc: nvim_proc });
nvim.command('vsp | vsp | vsp');
const windows = await nvim.windows;
assert.deepStrictEqual(windows.length, 4);
assert.ok(windows[0] instanceof nvim.Window);
nvim.window = windows[2];
const win = await nvim.window;
assert.ok(win.id !== windows[0].id);
assert.deepStrictEqual(win.id, windows[2].id);
const buf = await nvim.buffer;
assert.ok(buf instanceof nvim.Buffer);
const lines = await buf.lines;
assert.deepStrictEqual(lines, []);
await buf.replace(['line1', 'line2'], 0);
const newLines = await buf.lines;
assert.deepStrictEqual(newLines, ['line1', 'line2']);
if (nvim_proc.disconnect) {
nvim_proc.disconnect();
}
nvim.quit();
while (nvim_proc.exitCode === null) {
await new Promise(resolve => setTimeout(resolve, 100))
console.log('waiting for Nvim (pid %d) to exit', nvim_proc.pid);
}
console.log('Nvim exit code: %d', nvim_proc.exitCode);
})();
Neovim supports remote plugins, which are plugins implemented as Nvim API clients. This package contains both the "API client" (which talks to nvim) and "remote plugin host" (which discovers and runs Nvim node.js remote plugins).
You can define a remote plugin as a file or folder in an rplugin/node/
directory on Nvim's 'runtimepath'.
If the plugin is a folder, the main
script from package.json
will be loaded.
The plugin must export a function which takes a NvimPlugin
object as its only parameter. You may then register autocmds, commands and functions by calling methods on the NvimPlugin
object.
Avoid heavy initialisation or async functions at this stage, because Nvim may only be collecting information about your plugin without wishing to actually use it.
Instead, wait for one of your autocmds, commands or functions to be called before starting any processing.
See examples/
for remote plugin examples.
NvimPlugin.nvim
This is the nvim api object you can use to send commands from your plugin to nvim.
NvimPlugin.setOptions(options: NvimPluginOptions);
interface NvimPluginOptions {
dev?: boolean;
alwaysInit?: boolean;
}
Set your plugin to dev mode, which will cause the module to be reloaded on each invocation.
alwaysInit
will always attempt to attempt to re-instantiate the plugin. e.g. your plugin class will
always get called on each invocation of your plugin's command.
NvimPlugin.registerAutocmd(name: string, fn: Function, options: AutocmdOptions): void;
NvimPlugin.registerAutocmd(name: string, fn: [any, Function], options: AutocmdOptions): void;
interface AutocmdOptions {
pattern: string; // See `:help autocmd-pattern`.
eval?: string; // Vimscript expression evaluated by the Nvim peer.
sync?: boolean; // Force blocking (non-async) behavior.
}
Registers an autocmd for the event name
, calling your function fn
with options
. Pattern is the only required option. If you wish to call a method on an object you may pass fn
as an array of [object, object.method]
.
By default autocmds, commands and functions are all treated as asynchronous and should return Promises
(or should be async
functions).
NvimPlugin.registerCommand(name: string, fn: Function, options?: CommandOptions): void;
NvimPlugin.registerCommand(name: string, fn: [any, Function], options?: CommandOptions): void;
interface CommandOptions {
sync?: boolean; // Force blocking (non-async) behavior.
range?: string; // See `:help :range`.
nargs?: string; // See `:help :command-nargs`.
}
Registers a command named by name
, calling function fn
with options
. This will be invoked from nvim by entering :name
in normal mode.
NvimPlugin.registerFunction(name: string, fn: Function, options?: NvimFunctionOptions): void;
NvimPlugin.registerFunction(name: string, fn: [any, Function], options?: NvimFunctionOptions): void;
interface NvimFunctionOptions {
sync?: boolean; // Force blocking (non-async) behavior.
range?: string; // See `:help :range`.
eval?: string; // Vimscript expression evaluated by the Nvim peer.
}
Registers a function with name name
, calling function fn
with options
. This will be invoked from nvim by entering eg :call name()
in normal mode.
For debugging and configuring logging, you can set the following environment variables which are used by the neovim
package (or nvim
itself where noted):
NVIM_NODE_HOST_DEBUG
: Spawns the node process that calls neovim-client-host
with --inspect-brk
so you can have a debugger.
Pair that with this Node Inspector Manager Chrome pluginwinston
through the logger
module. This package replaces console
with this interface.
NVIM_NODE_LOG_LEVEL
: Sets the logging level for winston. Default is debug
.
Available levels: { error: 0, warn: 1, info: 2, verbose: 3, debug: 4, silly: 5 }
NVIM_NODE_LOG_FILE
: Sets the log file path.NVIM_LISTEN_ADDRESS
:
$ NVIM_LISTEN_ADDRESS=/tmp/nvim nvim
// `scripts/nvim` will detect if `NVIM_LISTEN_ADDRESS` is set and use that unix socket
// Otherwise will create an embedded `nvim` instance
require('neovim/scripts/nvim').then((nvim) => {
nvim.command('vsp');
});
See the tests and scripts
for more examples.
After cloning the repo, run npm install
to install dev dependencies. The main neovim
library is in packages/neovim
.
npm run build && NVIM_NODE_LOG_FILE=log npm run test
Only maintainers of the neovim NPM package can publish a release. Follow these steps to publish a release:
CHANGELOG.md
.# Choose major/minor/patch as needed.
npm version --no-git-tag-version patch
npm version -w packages/neovim/ patch
git add package*.json packages/neovim/package.json
git commit -m 'release'
# Note: this copies the top-level README.md/CHANGELOG.md to packages/neovim/.
npm run publish:neovim
export _VERSION=$(grep -o 'version": "[^"]\+' packages/neovim/package.json | sed 's/.*"//')
git tag "v${_VERSION}"
git push --tags
git push
CHANGELOG.md
.npm version --no-git-tag-version prerelease --preid dev
npm version -w packages/neovim/ --no-git-tag-version prerelease --preid dev
git add package*.json packages/neovim/package.json
git commit -m bump
git push
The docs website is currently not automated. Follow these steps to regenerate it:
npm run doc -w packages/neovim
git checkout gh-pages
shopt -s extglob
git rm -r !(node_modules|packages)
mv packages/neovim/doc/* .
rm -r packages/
git add !(node_modules|packages)
git commit -m 'publish docs'
git push origin HEAD:gh-pages
FAQs
Nvim msgpack API client and remote plugin provider
The npm package neovim receives a total of 7,146 weekly downloads. As such, neovim popularity was classified as popular.
We found that neovim demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.