Unleashing the Power of NCM: Safeguarding Node.js Applications with Next-Generation Security in N|Solid

In the world of Node.js, application development, speed, flexibility, and scalability are critical for modern software development. However, the risk of vulnerabilities and security breaches looms with the increasing reliance on open-source Node packages. NCM (NodeSource Certified Modules) is the next-generation security solution that empowers Node.js developers to safeguard their applications easily and confidently.

This article will explore how NCM, a key N|Solid platform feature, revolutionizes how Node.js applications are secured, offering advanced security features, enhanced visibility, and peace of mind. Get ready to unleash the power of NCM and take your Node.js applications to new heights of security and reliability with N|Solid.

_Image 1 – Security Vulnerabilities in N|Solid View
_

Don’t miss out on this opportunity to try N|Solid for free and unlock the full potential of your Node.js applications.✍️ Sign up now and take your monitoring to the next level!

What is N|Solid?

_Image 2 – N|Solid Product View
_

N|Solid provides enhanced security for Node.js applications in production environments. It is built on top of the Node.js runtime. It provides a secure environment for running Node.js applications and advanced features such as worker threads monitoring, memory leak detection, and CPU profiling. We have +15 features in our product, including OpenTelemetry support, SBOM integration, and Machine Learning capabilities. Discover More HERE ‘__Top 10 N|Solid —APM for Node— features you needed to use__’ – HERE: ???????? nsrc.io/TopNSolidFeatures.

N|Solid offers many benefits over the standard Node.js runtime, including improved security through features like runtime vulnerability scanning, access control, and enhanced monitoring capabilities that allow developers to identify and address issues in real-time.

N|Solid is well-suited for enterprise applications requiring high performance, scalability, and security levels. It is widely used in finance, healthcare, and e-commerce. It is developed and maintained by __NodeSource__, a company specializing in enterprise-grade Node.js solutions.

In the previous section, we discussed N|Solid as a solution that provides enhanced security for Node.js applications in production environments. Let’s discuss the difference between NSolid Console, N|Solid Runtime, and N|Solid SaaS. It’s important to differentiate between these components for several reasons, including functionality, user experience, and flexibility.

What is the difference between NSolid Console, N|Solid Runtime, and N|Solid SaaS?

Differentiating between the Console, Runtime, and SaaS setup in N|Solid is essential for a few reasons: functionality, user experience, and flexibility.

Users can deploy N|Solid in multiple ways, including using the N|Solid Console, N|Solid Runtime, or N|Solid SaaS setup, depending on their requirements and infrastructure setup. It is essential to provide distinct functionalities to enhance user experience and offer flexibility in deployment options, allowing scalability, customization, and integration with existing workflows. Here’s a brief description of each:

N|Solid Runtime is the runtime environment for Node.js applications. It includes a modified version of the Node.js runtime, enhanced with additional security, monitoring, and debugging features. These features include advanced profiling and tracing capabilities, heap and CPU profiling, and runtime vulnerability scanning.
???????? https://bit.ly/NSolidRuntime-npm

_Image 3 – N|Solid Runtime Installation
_

__N|Solid Console__, on the other hand, is a web-based dashboard that provides a graphical user interface for monitoring and managing Node.js applications running on N|Solid Runtime. It lets users view their applications’ real-time metrics and performance data, monitor resource utilization, and set alerts for specific events or thresholds. N|Solid Console also provides features for managing user access and permissions, configuring application settings, and integrating with third-party tools and services. It can manage multiple N|Solid Runtimes across a distributed environment, making it ideal for large-scale enterprise deployments.
???????? https://nsrc.io/NSolidConsole

_Image 4 – N|Solid Console Overview
_

__N|Solid SaaS__: N|Solid also offers a SaaS (Software-as-a-Service) setup so users can leverage N|Solid’s enhanced security and performance features without managing their own infrastructure. With N|Solid SaaS, users can simply sign up for a subscription and use N|Solid’s features through a cloud-based service without needing on-premises installation or maintenance. ???????? https://nsrc.io/NSolidSaaS

_Image 5 – N|Solid SaaS Overview
_

N|Solid offers multiple deployment options; these components provide distinct functionalities, user experiences, and deployment flexibilities, catering to the diverse needs of enterprise Node.js applications.

But, What about NCM?

NodeSource Certified Modules (NCM) is another product developed by NodeSource that provides you and your teams with actionable insights into the risk levels of using third-party packages. Using a series of tests, we score packages on npm to look for several weighted criteria. With NCM CLI, you can scan your projects for existing security vulnerabilities, license concerns, code risk, and code quality. This helps you understand the level of risk exposure and how to mitigate it. NodeSource Certified Modules (NCM) also work in offline mode. Explore Further ‘__Avoiding npm substitution attacks using NCM__’ HERE ????????https://nsrc.io/AvoidAttackswithNCM

_Image 6 – NCM CLI Report
_

NodeSource Certified Modules (NCM) is a security, compliance, and curation tool around the 3rd-Party Node.js & JavaScript package ecosystem. It is designed to be used with npm to provide protection against known security vulnerabilities and potential license compliance issues and provide general quality or risk assessment information to improve your ability to work with the 3rd-Party ecosystem.

Since the release of N|Solid 4.1.0, we have consolidated NCM into a single product with NCM’s features being pulled into N|Solid Runtime, N|Solid SaaS, and the N|Solid Console for optimal user experience. It also provides alerts and notifications when new vulnerabilities are discovered in modules used by an organization’s applications and helps users quickly identify and remediate any potential security risks.NCM is a valuable tool for organizations that rely on Node.js and open-source modules, helping to ensure that their applications are secure, reliable, and compliant with industry standards and regulations.

NCM now assesses packages based on multiple attributes: security, compliance, risk, and quality. These attributes are combined to generate an overall risk level for each package, providing valuable insights to manage third-party code in your Node.js applications effectively. With NCM’s scoring system, you can:

__Manage acceptable risk levels__: NCM helps you assess the risk associated with third-party packages by providing an overall risk level for each package. This allows you to make informed decisions about the level of risk you are willing to accept in your application.
__Understand security vulnerabilities__: NCM identifies and highlights security vulnerabilities in third-party modules, allowing you to understand the severity of the vulnerabilities and take appropriate actions to address them in your code.
__Manage license and compliance risks__: NCM helps you identify potential license and compliance risks introduced by third-party modules, ensuring that your application adheres to licensing requirements and compliance standards.
__Identify potential risk vectors__: NCM goes beyond known security vulnerabilities and identifies potential risks that may not have surfaced in security vulnerabilities yet. This helps you proactively identify and address potential risks in your code.
__Improve code quality__: NCM provides insights into quality attributes that align with best practices, helping you improve the quality of your code and make it more manageable and secure.

Together, these attributes in NCM’s scoring system (security, compliance, risk, and quality.) provide a comprehensive assessment of third-party packages, enabling you to effectively manage and secure your Node.js applications by addressing security vulnerabilities, managing compliance risks, assessing package risk, and provides insights to improve code quality. Find Out More about ‘Vulnerability Scanning & 3rd-Party Modules Certification’- HERE ???????? nsrc.io/VulnerabilityScanningNS

The Importance of Node.js Application Security

Selecting the right tools and applications for your developer pipeline requires careful consideration of your team’s workflow and project needs. This might involve assessing your tech stack, deployment processes, and the number of steps in your pipeline and identifying areas where guardrails can be implemented to improve security and reliability.

_Image 7 – NCM Criteria
_

Fortunately, numerous tools and applications are available to assist in managing your pipeline and ensuring the security and compliance of your applications. One powerful tool in this regard is NCM (NodeSource Certified Modules). NCM is a comprehensive security, compliance, and curation tool that offers advanced capabilities for managing dependencies in Node.js applications. By integrating NCM into your pipeline, you can effortlessly scan for vulnerabilities, track package dependencies, and ensure compliance with licensing requirements.

NCM enables you to elevate your pipeline to the next level, enhancing your application’s performance, reliability, and security while safeguarding against __SUPPLY CHAIN ATTACKS__. With the consolidation of NCM into N|Solid, you can now seamlessly access these powerful capabilities through the N|Solid Console for a streamlined user experience.

Note: Supply chain attacks are a type of cyber attack that targets the weakest link in a software supply chain. Instead of directly attacking a target, hackers infiltrate a trusted third-party vendor, supplier, or service provider to gain access to their customer’s systems and data. This allows the attackers to distribute malicious code or compromise software updates, which can then infect the entire supply chain and cause widespread damage. Supply chain attacks can be difficult to detect and prevent, making them a growing threat to organizations of all sizes and industries.

The importance of NCM

The consolidation of NCM 2 into N|Solid represents a significant milestone in providing a comprehensive solution for ensuring the security, reliability, and performance of Node.js applications. With features such as:

Projects & Applications Monitoring – https://nsrc.io/ProjectApplicationsMonitoringNS

Process Monitoring – https://nsrc.io/ProcessMonitoringNS

CPU Profiling – https://nsrc.io/CPUProfilingNS

Worker Threads Monitoring – https://nsrc.io/WorkerThreadsNS

Capture Heap Snapshots – https://nsrc.io/HeapSnapshotsNS

Memory Anomaly Detection – https://nsrc.io/MemoryAnomalyNS

Vulnerability Scanning & 3rd party Modules Certification – https://nsrc.io/VulnerabilityScanningNS
HTTP Tracing Support – https://nsrc.io/HTTPTracingNS

Global Alerts & Integrations – https://nsrc.io/GlobalAlertsIntegrationsNS

Distributed Tracing – https://nsrc.io/DistributedTracingNS

Open Telemetry Support – nsrc.io/AIOpsNSolid

SBOM Support – nsrc.io/SBOM-NSolid

Machine Learning Support – nsrc.io/ML-NSolid

N|Solid offers a robust and all-encompassing solution for managing the entire lifecycle of Node.js applications. By incorporating NCM’s powerful capabilities for security, compliance, and curation, N|Solid empowers developers and organizations to proactively identify and address vulnerabilities, track dependencies, and ensure licensing compliance, ultimately elevating the overall performance, reliability, and security of their applications. With N|Solid, organizations can confidently build and deploy Node.js applications with peace of mind, knowing their software is protected against potential risks and supply chain attacks.

Conclusion:

Securing Node.js applications is paramount in today’s software development landscape. With the powerful features of NSolid, including the N|Solid Console and N|Solid Runtime, combined with the cutting-edge security capabilities of NCM, developers can safeguard their Node.js applications with next-generation security measures or simply leaving the maintenance and infrastructure to us by selecting our N|Solid SaaS option. By leveraging the power of NCM in the N|Solid platform, developers can proactively mitigate vulnerabilities and ensure the reliability and stability of their Node.js applications. Embrace the power of NCM in N|Solid today and unleash the full potential of your Node.js applications with advanced security measures.

NodeSource’s Products:

N|Solid Runtime is the Node.js runtime environment with enhanced security, monitoring, and debugging features.

N|Solid Console is a web-based dashboard for managing and monitoring Node.js applications running on N|Solid Runtime.
__N|Solid SaaS__: Benefit from N|Solid’s advanced security and performance features through a cloud-based subscription service, eliminating the need for on-premises installation or maintenance.

NCM is a cutting-edge security feature integrated into the N|Solid platform that provides continuous monitoring, vulnerability scanning, and risk assessment of open-source Node.js packages used in Node.js applications.

To get the best out of Node.js and experience the benefits of its integrated features, including OpenTelemetry support, SBOM integration, and Machine Learning capabilities. ✍️ Sign up for a free trial and see how N|Solid can help you achieve your development and operations goals. #KnowyourNode

Introducing nsuv

Introducing nsuv

nsuv is a C++ wrapper around libuv with the main goal of supporting compile-time type safety when propagating data.

You can find the open source package here: https://github.com/nodesource/nsuv

Here at NodeSource we are focused on fixing issues for the enterprise. This includes adding functionality and features to Node.js that are useful for enterprise-level deployments but would be difficult to upstream. One is the ability to execute commands remotely on Worker threads without the addition of running the inspector, such as capturing CPU profiles or heap snapshots. Another feature necessary to make Node.js more reliable in production is the ability to record and send metrics without being at the mercy of a busy event loop.

To achieve these, we run a separate thread that receives commands and gathers metrics from each Node.js thread. The locks and data queues in the separate thread are managed by libuv. As the codebase grew, usability issues began to come up, such as remembering the correct type of each void pointer and keeping track of the lifetime of the many shared locks and resources. Our solution was to write a wrapper for libuv to alleviate these problems.

We had a lot of existing libuv code and didn’t want to rewrite everything from scratch. So we wrote a template class library that inherits from each libuv handle or request type and uses the curiously recurring template pattern (CRTP) for inheritance. Doing so made it possible to write a wrapper that serves as a drop-in replacement, allowing for incremental improvements while supplementing the wrapper’s API with what was needed.

N|Solid has a zero-failure tolerance, so none of our code can accidentally terminate your process. One way we do this is to try our best not to perform additional allocations. If an allocation is necessary, it always does with a strong exception guarantee, which is then caught and returned as a libuv error code.

We have also enabled compile time warnings when returned error codes aren’t handled. While developing nsuv, we analyzed many existing C++ projects that use libuv and discovered that most of them assume the state of the application and lack sufficient error handling in case something unexpected occurs. This can be especially painful when working with asynchronous code, but we understand that not everyone requires the same level of caution. It can be disabled by defining NSUV_DISABLE_WUR in your flags.

Getting Started

The following code example shows the execution of a simple libuv timer, and the only change was to turn the uv_timer_t to a nsuv::ns_timer instance while still being able to use the original libuv APIs:

static void timer_cb(uv_timer_t* handle) {
Foo* foo = static_cast<Foo*>(handle->data);
delete foo;
uv_close(reinterpret_cast<uv_handle_t*>(handle), nullptr);
}

static void call_timer() {
ns_timer timer;
Foo* foo = new Foo();

timer.data = foo;
uv_timer_init(uv_default_loop(), &timer);
uv_timer_start(&timer, timer_cb, 1000, 0);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
}

As you can see, there’s no need to cast timer before being passed to libuv’s timer function since ns_timer is a derived class of uv_timer_t and upcasting is implicit. It offers the first step in converting code to be more type-safe and improve overall usability. Improvements can be made incrementally from here. Below we take advantage of the CRTP and use it to downcast the uv_timer_t to the nsuv counterpart after using libuv’s timer API:

static void timer_cb(uv_timer_t* handle) {
// Downcast the libuv handle to its nsuv counterpart.
ns_timer* timer = ns_timer::cast(handle);
// Convenience method to retrieve and cast data.
Foo* foo = timer->get_data<Foo>();

delete foo;
timer->close();
}

While this is a good first step, it still requires we know what the data value should be cast to. The call to get_data() only serves as a convenience method for easier casting.

Passing Data

One of the most painful parts of working with libuv was ensuring we didn’t accidentally cast a void pointer to the wrong type from a specific queue. While this could be verified by hand, having the compiler tell us if we did it wrong would have been more reassuring.

To accomplish this, we wrapped libuv in a way that allows any function that takes a callback to be passed an arbitrary pointer. That pointer is then passed along as an argument in the callback’s parameters. Preventing us from needing to use the uv_handle_t::data property and ensuring the callback always has the correct pointer type.

Below we have fully converted the previous code to use nsuv. As you can see, the pointer that would have been stored in the data parameter can now be passed to the method, making it available as an argument in the callback.

static void call_timer() {
ns_timer timer;
Foo* foo = new Foo();
int r;

r = timer.init(uv_default_loop());
//check r
r = timer.start(+[](ns_timer* handle, Foo* foo) {
delete foo;
handle->close();
}, 1000, 0, foo);
// check r

uv_run(uv_default_loop(), UV_RUN_DEFAULT);
}

For the sake of the example, a C++ lambda function was used. Remember that when passing a lambda function, it needs to be converted to a plain old function pointer using the + operator.

Also notice that we are assigning and handling all return values from each call. As mentioned above, the compiler will warn us if we do not check each call’s return codes. For simplicity of future examples, the return value will be assigned but not include a comment that it needs to be checked.

Locks

Because of all the communication between threads, mutexes were heavily used. To make things simpler, we added a couple of APIs for convenience. The first API of note is that init() accepts an optional boolean value. If true is passed in, the mutex is automatically destroyed when the destructor is called. The other was to add an API for scoped locking.

static void try_mutex() {
ns_mutex mutex;
// The optional boolean argument sets if the mutex should be
// automatically destroyed in the destructor.
int r = mutex.init(true);
// Convenience class to create scoped locks. Accepts either a
// pointer or reference.
{
ns_mutex::scoped_lock lock(mutex);
}
}

Having a mutex call destroy() in the destructor was kept false by default to maintain parity with the libuv API and prevent surprises while migrating to nsuv.

Example Usage

At first, we only implemented the libuv APIs that were necessary for us to use internally, but since deciding to open source the library we have begun to add as much of the remaining libuv APIs as possible. But despite not having yet ported the entire libuv API, it’s still possible to take advantage of what has been done. The following is an example from a test that includes the checks to demonstrate how class instances are being passed around.

#include “nsuv-inl.h”

using namespace nsuv;

ns_tcp client;
ns_tcp incoming;
ns_tcp server;
ns_connect<ns_tcp> connect_req;
ns_write<ns_tcp> write_req;

static void alloc_cb(ns_tcp* handle, size_t, uv_buf_t* buf) {
static char slab[1024];
assert(handle == &incoming);

buf->base = slab;
buf->len = sizeof(slab);
}

static void read_cb(ns_tcp* handle, ssize_t, const uv_buf_t*) {
assert(handle == &incoming);

handle->close();
client.close();
server.close();
}

static void write_cb(ns_write<ns_tcp>* req, int) {
assert(req == &write_req);
// Retrieve a reference to the uv_buf_t array as a std::vector.
assert(req->bufs().size() == 2);
}

static void connection_cb(ns_tcp* server, int) {
int r;
r = incoming.init(server->get_loop());
r = server->accept(&incoming);
r = incoming.read_start(alloc_cb, read_cb);
}

static void connect_cb(ns_connect<ns_tcp>* req, int, char* data) {
static char bye_ctr[] = “BYE”;
uv_buf_t buf1 = uv_buf_init(data, strlen(data));
uv_buf_t buf2 = uv_buf_init(bye_ctr, strlen(bye_ctr));
// Write to the handle attached to this request and pass along data
// by constructing a std::vector.
int r = req->handle()->write(&write_req, { buf1, buf2 }, write_cb);
}

static void do_listen() {
static char hello_cstr[] = “HELLO”;
struct sockaddr_in addr_in;
struct sockaddr* addr;
int r;

r = uv_ip4_addr(“127.0.0.1”, 9999, &addr_in);
addr = reinterpret_cast<struct sockaddr*>(&addr_in);

// Server setup.
r = server.init(uv_default_loop());
r = server.bind(addr, 0);
r = server.listen(1, connection_cb);

// Client connection.
r = client.init(uv_default_loop());
r = client.connect(&connect_req, addr, connect_cb, hello_cstr);

uv_run(uv_default_loop(), UV_RUN_DEFAULT);
}

The request types ns_write and ns_connect are also used in the above example. They inherit from uv_write_t and uv_connect_t respectively, and can be upcast and downcast the same way as handles. Each request type API is templated to identify which handle is being used and can return the correct handle type.

While the write() method does accept a uv_buf_t[] array, we’ve also added the ability to pass in a std::vector of buffers for ease of use. Once the request is complete, the list of written buffers can be retrieved via the ns_write::buf() API as a reference to the std::vector that’s stored internally.

Conclusion

One goal when creating nsuv was to reduce cognitive load by mimicking the libuv API naming and structure while adding safety features offered by C++. We’ve made it easy to transition existing projects to nsuv. By open-sourcing nsuv, we hope to give developers more confidence that their code will behave as expected when expected.

There is near zero runtime overhead using nsuv. The template function proxy pattern used can be completely optimized out by modern compilers. Combining that with the ability to enforce type checks at compile time, I won’t be using libuv in C++ without nsuv going forward.

Using nsuv is as simple as including the two header files from the project repository. We are still working on getting complete coverage of the libuv API and hope the community can help us decide what to work on next. We are also working on porting all applicable tests from libuv to nsuv, which can serve as usage examples. We hope that you’ll find nsuv as useful as we have.

NodeSource has delivered Node.js fresh to your Linux system via your package manager within hours, minutes, days, or weeks. For NodeSource, sustaining the community is essential because we want to support more people using Linux to have Node.js in production.

Also, we are looking for more community involvement in the project. Help will be appreciated! So if you have ideas or solutions or want to help us continue supporting open source, you can contribute to this GitHub Repo.

Continue the conversation with NodeSource here:
Twitter
LinkedIn
Github
As always, the best place to contact us is via our website or [email protected]

Ready for more?

If you are looking for NodeSource’s Enterprise-grade Node.js platform, N|Solid, please visit https://downloads.nodesource.com/. For detailed information on installing and using N|Solid, please refer to the N|Solid User Guide.