Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Simple utility for context propagation within Javascript applications and libraries. Loosely based on the ideas behind React's context, allows you to achieve the same goals (and more) without actually using react. It allows you to keep reference for shared variables, and access them down in your function call even if not declared in the same scope.
The way context works is quite simple. Creating a context initializes a closure with a context storage object. When you run your context call, it takes your values, and places them in the context's closure. When your function finishes running, the context is cleared.
The reason the context is cleared after its run is so that items can't override one another. Assume you have two running the same async function twice, and it writes to the context, expecting to read the value somewhere down the line. Now you have two competing consumers of this single resource, and one eventually get the wrong value since they both read from and write to the same place.
The context package exports these two functions:
createContext
: Creates a new context.createCascade
: Creates a new cascading context.Create context is the minimal implementation of context. It allows propagation of values down in your function call.
createContext takes a single argument - defaultContextValue. This value is used when not within a running context.
Argument | Type | Optional? | Description |
---|---|---|---|
defaultContextValue | any | Yes | The default value to use when not within a running context. |
createContext
returns an object containing the following functions:
use
: Returns the current context value, or the default value when not within a running context.useX
: Returns the current context, throws an error if not within a running context or the context is undefined. useX
will throw even if a default value is provided.run
: Runs the context, passing the given value into the context.Note About Typescript Usage
For convenience, use
assumes we're always inside a context. If you want to have runtime safety, you can use useX
instead to make sure you're explicitly using a defined context.
const context = createContext(0); // Create a context with a default value of 0.
function myFunc() {
context.run(100, someOtherFunc); // Run the context with a value of 100.
}
function someOtherFunc() {
const number = context.use(); // Returns the value of the context.
}
createCascade
is a more advanced version of createContext
that allows you to create cascading contexts. It assumes the value is always an object, and when nesting context layers, it merges their values together.
createCascade
does not take a default value, but an initializer function instead. This initializer is called on each run
call, and it allows you to modify or augment the context value being passed down. The init function is passed the context object run was called with, and the parent context if it was called within a previously created context. The init function needs to return the desired context object.
Argument | Type | Optional? | Description |
---|---|---|---|
initializer | Function | Yes | The initializer function to use when creating a new context. |
The initializer function can either return the the next context object. If null is returned itself, the initializer will take no effect. The initializer receives the context object, and the parent context object, if present.
createCascade
returns an object containing the following functions:
use
: Returns the current context value.useX
: Returns the current context, throws an error if not within a running context.run
: Runs the context, passing the given value into the context. Merges the given value with the parent context if it exists, while not overriding the parent context.bind
: Binds a given function to the context. Allows for delayd execution of a function as if it was called within the context.createCascade((ctx, parentContext) => {
if (parentContext === null) {
// we're at the top level
// so let's add a default cart object
return Object.assign(ctx, {
cart: [],
});
}
// we're in a sub context, so we already have those default values.
return ctx;
});
Runs a callback function within the context. It takes an object referencing the data you want to store within your context, and a callback function to run.
// myFunction.js
import ctx from './ctx';
import sayUsername from './sayUsername';
function myFunction(username) {
return ctx.run({ username }, sayUsername);
}
If run
is called within a different run
call, the context values will be merged when the callback is run. When we exit our callback, the context will be reset to the values it had before the call.
These are the main ways to access the context within your code. They return the current object that stored within the context, and differ in the way they handle calls outside of the context.
run
call.run
call.
// sayUsername.js
import ctx from './ctx';
function sayUsername() {
const context = ctx.use(); // { username: 'John Doe' }
if (!context) {
// we're not within a `run` call. This function was called outside of a running context.
return "Hey, I don't know you, and this is crazy!";
}
return `Hello, ${context.username}!`;
}
// handleCart.js
import ctx from './ctx';
function handleCart() {
const context = ctx.useX(
'handleCart was called outside of a running context',
); // { cart: { items: [ 'foo', 'bar' ] } }
// This throws an error if we're not within a `run` call.
// You should catch this error and handle it somewhere above this function.
return `You have ${context.cart.items.length} items in your cart.`;
}
Bind a function to a context. It takes an object referencing the data you want to store within your context, and a callback function to run. It returns a function that can be called with the same arguments as the original function. The function will then internally call run
with the same arguments, and return the result of the callback function, so it is useful if you want to reference a context after it was closed, for example - when running an async function.
// getProductData.js
import ctx from './ctx';
function getProductData(productId) {
return ctx.bind({ productId }, handleProductData);
fetchProduct(productId).then(data => {
handleProductData(data);
// will run the function handleProductData within our context, even though there is no context running at the moment.
});
}
both createContext
and createCascade
have full typescript support. To gain the full benefits of typescript within your context, it is best to annotate your context with its types:
const someContext = createContext<number>(0);
const someCascadeContext = createCascade<{
username: string;
firstName: string;
middleName?: string;
lastName: string;
age: number;
}>();
This meakes sure that all the functions (run
, use
, useX
and bind
) will be aware of these types, and either accept them as inputs, or add them to the return value.
Working with context inside within async code may lead to unexpected results when we don't fully consider what's happening. Trying to call your context from the async part of your code will probably return null
instead of your values.
This is known and expected behavior. Context is a synchronous context propagation tool that completely relies on the synchronous nature of function calls in JS - this is exactly what allows context to run.
The async parts of your function are actually not executed along with your sync code, and even though you "await" it, the browser carries on and allows other code to run in between instead of blocking execution until your async code is complete.
There are multiple strategies of handling async functions with context.
Pulling your values from context right before your async call This is the most obvious and easiest to achieve, though not always what you need. The basic idea is that you take whatever you need from the context when it is still available to you.
context.bind or context.run your async function to the context you extracted This is the next logical step - you have a function that you know should run later with your context. You can bind your context to it for delayed execution. When your function runs later down the line within your asynchronous code, internally it will still have access to whatever you bound to it.
FAQs
Unknown package
The npm package context receives a total of 6,740 weekly downloads. As such, context popularity was classified as popular.
We found that context demonstrated a healthy version release cadence and project activity because the last version was released less than 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 researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.