Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Tiny transport agnostic JSON-RPC 2.0 client and server which can work both in NodeJs, Browser, Electron etc
Yet another JSON-RPC library? Why do we need it? The reality, that there is no transport agnostic JSON-RPC library for nodejs. One of our projects required JSON RPC over MQTT and unfortunetely we were not able to find a good solution.
Key issues with existing libraries:
Mole-RPC solves all of the issues described above.
This module is transport agnostics. So, you can choose any transport you need
In this example, we use WebSocketServer for RPC server but you you can use simple WS server transport as well. This can be useful for the case when server connects to client (you can bypass firefall in this way).
Server
const MoleServer = require('mole-rpc/MoleServer');
const TransportServerWSS = require('mole-rpc-transport-ws/TransportServerWSS');
const WebSocket = require('ws');
const WSS_PORT = 12345;
function sum(a, b) { return a + b }
function multiply(a, b) { return a * b }
async function main() {
const server = new MoleServer({
transports: prepareTransports()
});
server.expose({ sum, multiply });
await server.run();
}
function prepareTransports() {
return [
new TransportServerWSS({
wss: new WebSocket.Server({
port: WSS_PORT
})
})
];
}
main().catch(console.error);
Client (with Proxy support)
const MoleClient = require('mole-rpc/MoleClientProxified');
const X = require('mole-rpc/X');
const TransportClientWS = require('mole-rpc-transport-ws/TransportClientWS');
const WebSocket = require('ws');
const WSS_PORT = 12345;
async function main() {
const client = new MoleClient({
requestTimeout: 1000,
transport: prepareTransport()
});
try {
console.log( await client.sum(2, 3) );
} catch (error) {
if (error instanceof X.ExecutionError) {
console.log('ERROR', error.data);
} else {
throw error;
}
}
}
function prepareTransport() {
return new TransportClientWS({
wsBuilder: () => new WebSocket(`ws://localhost:${WSS_PORT}`)
});
}
main().then(console.log, console.error);
If you use modern JavaScript you can use proxified client. It allows you to do remote calls very similar to local calls
import MoleClientProxified from 'mole-rpc/MoleClientProxified';
// choose any transports here
// https://www.npmjs.com/search?q=keywords:mole-transport
const transport = new TransportClient();
const calculator = new MoleClientProxified({ transport });
const result1 = await calculator.sum(1, 3);
const result2 = await calculator.asyncSum(2, 3);
// Send JSON RPC notification (fire and forget)
// server will send no response
await calculator.notify.sum(3, 2);
import MoleClient from 'mole-rpc/MoleClient';
// choose any transports here
// https://www.npmjs.com/search?q=keywords:mole-transport
const transport = new TransportClient();
const client = new MoleClient({ transport });
const result1 = await client.callMethod('sum', [1, 3]);
const result2 = await client.callMethod('sum', [2, 3]);
// Send JSON RPC notification (fire and forget)
// server will send no response
await client.notify('sum', [2, 3]);
You can expose instance directly. Methods which start with underscore will not be exposed. Built-in methods of Object base class will not be exposed.
import MoleServer from 'mole-rpc/MoleServer';
class Calculator {
sum(a, b) {
return a + b;
}
asyncSum(a, b) {
return new Promise((resolve, reject) => {
resolve(this.sum(a, b));
});
}
_privateMethod() {
// will not be exposed
}
}
const calculator = new Calculator();
// choose any transports here
// https://www.npmjs.com/search?q=keywords:mole-transport
const transports = [new TransportServer()];
const server = new MoleServer({ transports: [] });
server.expose(calculator);
await server.run();
You can expose functions directly
import MoleServer from "mole-rpc/MoleServer";
function sum(a, b) {
return a+b;
}
function asyncSum(a, b) {
return new Promise((resolve, reject) {
resolve( sum(a, b) );
});
}
// choose any transports here
// https://www.npmjs.com/search?q=keywords:mole-transport
const transports = [ new TransportServer() ];
const server = new MoleServer({ transports });
server.expose({
sum,
asyncSum
});
await server.run();
When an rpc call encounters an error, the server will return an object with an error code. See JSON RPC 2.0 Specification for details.
Getting an error Mole RPC Client will throw (reject promise) a corresponding exception.
List of available exception classes:
Every exception object has following properties:
How to return an error from method?
Nothing special required. Just reject promise or throw an exception.
function divide(a, b) {
if (b == 0) throw "devision by zero";
// throw 'new Error("devision by zero")' will behave the same
return a / b;
}
function loadUser(userId) {
...
// you can throw an object
return Promise.reject({ error: 'NOT_EXISTING_USER'})
}
server.expose({ divide, loadUser });
How to handle the error?
Nothing special. Just catch the exception.
const X = require('mole-rpc/X');
async function main() {
...
try {
await client.divide(2, 3);
} catch (error) {
if (error instanceof X.ExecutionError) {
console.log('METHOD RETURNED ERROR', error.data);
} else if (error instanceof X.RequestTimout) {
console.log('METHOD EXCEEDED ALLOWED EXECUTION TIME');
} else {
throw error;
}
}
}
// Proxified client: explicit call
await calculator.callMethod.sum(1, 2); // the same as "calculator.sum(1, 2)"
// Can be usefull if your server method is a reserverd name.
// For example to make a call to remote "notify" method.
await proxifiedClient.callMethod.notify("Hello");
// Proxified client: run request in parallel
const promises = [
calculator.sum(1, 2);
calculator.notify.sum(1, 2);
];
const results = await Promise.all(promises);
// Simple client: run in parallel
const promises = [
client.callMethod('sum', [1, 2]);
client.notify('sum', [1, 2]);
];
const results = await Promise.all(promises);
// Simple client: run batch
const results = await client.runBatch([
// [methodName, params, mode]
['sum', [1, 3]],
['sum', [2, 5], 'notify'],
['sum', [7, 9], 'callMethod'], // "callMethod" is optional
]);
// Result structure
[
{success: true, result: 123},
null, // no response for notification
{success: false, error: errorObject}
];
To communicate with web worker, in most cases, you will try to simulate JSON RPC having "id", "method", "params" in each request and "id", "result" in each response.
With Mole RCP there is no need to use custom hacks. Just use mole-rpc-transport-webworker.
You have a device (or service) in local network and want to manage it. You cannot get to it from Internet, as the device is hidden behind NAT. But your device can connect to your internet server. So, with Mole RPC your device (RPC Server) can connect to the your internet server (RPC Client) and after that the internet server will be able to call methods on the devices hidden behind NAT.
Here is an example - https://github.com/koorchik/node-mole-rpc-transport-ws/tree/master/examples/server-connects-to-client
This case is rather hard to implement with other JSON RPC modules but with Mole RPC it works by design.
You have a lot microservices and you want allow them to communicate with each other. The best solutions here is to have a message broker. Mole RPC has MQTT transport which will allow to setup the communication easily.
Websocket is a good options for it. With websocker transport you can connect browser to server and the same time it suitable for connecting to server processes. It is not only option, you not limited to use any transport you wish.
You can pass multiple transports to MoleServer. This transport can be of different types. For example, you can expose the same methods via MQTT and WebSockets the same time.
Transports have simple API as possible, so it is very easy to add a new transport. MoleRPC has strong separation between protocol handler and transports. Transports know nothing about what is inside payload. Therefore, they are very simple. Usually, is is just two classes with 1-2 methods.
The best way to start is just to look at source code of existing implemenations - https://www.npmjs.com/search?q=keywords:mole-transport
Moreover, we have created an AutoTester for transports. Use AutoTester to cover 95% of cases and just add several tests to cover transport specific logic like reconnections etc.
FAQs
Transport agnostic spec compliant JSON RPC client and server
We found that dho-rpc demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer 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.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.