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.
Kareem is a lightweight middleware framework for handling pre and post hooks in JavaScript. It is often used in conjunction with Mongoose to manage middleware for MongoDB operations, but it can be used independently for any asynchronous control flow.
Pre Hooks
Pre hooks allow you to run functions before a specified event. In this example, a pre-save hook is defined and executed before the 'save' event.
const Kareem = require('kareem');
const hooks = new Kareem();
hooks.pre('save', function(next) {
console.log('This is a pre-save hook');
next();
});
hooks.execPre('save', null, function() {
console.log('Pre-save hooks executed');
});
Post Hooks
Post hooks allow you to run functions after a specified event. In this example, a post-save hook is defined and executed after the 'save' event.
const Kareem = require('kareem');
const hooks = new Kareem();
hooks.post('save', function() {
console.log('This is a post-save hook');
});
hooks.execPost('save', null, [function() {
console.log('Post-save hooks executed');
}]);
Parallel Hooks
Parallel hooks allow you to run multiple hooks in parallel. In this example, two pre-save hooks are defined to run in parallel before the 'save' event.
const Kareem = require('kareem');
const hooks = new Kareem();
hooks.pre('save', true, function(next, done) {
setTimeout(function() {
console.log('First parallel pre-save hook');
done();
}, 100);
});
hooks.pre('save', true, function(next, done) {
setTimeout(function() {
console.log('Second parallel pre-save hook');
done();
}, 50);
});
hooks.execPre('save', null, function() {
console.log('All parallel pre-save hooks executed');
});
The 'async' package provides powerful utilities for working with asynchronous JavaScript. It offers functions for parallel, series, and waterfall control flows, among others. While it is more general-purpose compared to Kareem, it can be used to achieve similar middleware-like behavior.
The 'middleware' package is a simple middleware framework for Node.js. It allows you to define and execute middleware functions in a stack-like manner. It is similar to Kareem in that it provides a way to manage pre and post hooks, but it is more focused on HTTP request/response cycles.
The 'hooker' package is a utility for hooking into JavaScript functions. It allows you to define pre and post hooks for any function, making it similar to Kareem. However, it is more focused on function hooking rather than middleware management.
Re-imagined take on the hooks module, meant to offer additional flexibility in allowing you to execute hooks whenever necessary, as opposed to simply wrapping a single function.
Named for the NBA's all-time leading scorer Kareem Abdul-Jabbar, known for his mastery of the hook shot
Much like hooks, kareem lets you define
pre and post hooks: pre hooks are called before a given function executes.
Unlike hooks, kareem stores hooks and other internal state in a separate
object, rather than relying on inheritance. Furthermore, kareem exposes
an execPre()
function that allows you to execute your pre hooks when
appropriate, giving you more fine-grained control over your function hooks.
hooks.execPre('cook', null, function() {
// ...
});
pre hook functions take one parameter, a "done" function that you execute when your pre hook is finished.
var count = 0;
hooks.pre('cook', function(done) {
++count;
done();
});
hooks.execPre('cook', null, function() {
assert.equal(1, count);
});
var count1 = 0;
var count2 = 0;
hooks.pre('cook', function(done) {
++count1;
done();
});
hooks.pre('cook', function(done) {
++count2;
done();
});
hooks.execPre('cook', null, function() {
assert.equal(1, count1);
assert.equal(1, count2);
});
If your pre hook function takes no parameters, its assumed to be fully synchronous.
var count1 = 0;
var count2 = 0;
hooks.pre('cook', function() {
++count1;
});
hooks.pre('cook', function() {
++count2;
});
hooks.execPre('cook', null, function(error) {
assert.equal(null, error);
assert.equal(1, count1);
assert.equal(1, count2);
});
Pre save hook functions are bound to the second parameter to execPre()
hooks.pre('cook', function(done) {
this.bacon = 3;
done();
});
hooks.pre('cook', function(done) {
this.eggs = 4;
done();
});
var obj = { bacon: 0, eggs: 0 };
// In the pre hooks, `this` will refer to `obj`
hooks.execPre('cook', obj, function(error) {
assert.equal(null, error);
assert.equal(3, obj.bacon);
assert.equal(4, obj.eggs);
});
Like the hooks module, you can declare "async" pre hooks - these take two
parameters, the functions next()
and done()
. next()
passes control to
the next pre hook, but the underlying function won't be called until all
async pre hooks have called done()
.
hooks.pre('cook', true, function(next, done) {
this.bacon = 3;
next();
setTimeout(function() {
done();
}, 5);
});
hooks.pre('cook', true, function(next, done) {
next();
var _this = this;
setTimeout(function() {
_this.eggs = 4;
done();
}, 10);
});
hooks.pre('cook', function(next) {
this.waffles = false;
next();
});
var obj = { bacon: 0, eggs: 0 };
hooks.execPre('cook', obj, function() {
assert.equal(3, obj.bacon);
assert.equal(4, obj.eggs);
assert.equal(false, obj.waffles);
});
You can also return a promise from your pre hooks instead of calling
next()
. When the returned promise resolves, kareem will kick off the
next middleware.
hooks.pre('cook', function() {
return new Promise(resolve => {
setTimeout(() => {
this.bacon = 3;
resolve();
}, 100);
});
});
var obj = { bacon: 0 };
hooks.execPre('cook', obj, function() {
assert.equal(3, obj.bacon);
});
acquit:ignore:end
hooks.execPost('cook', null, [1], function(error, eggs) {
assert.ifError(error);
assert.equal(1, eggs);
done();
});
hooks.post('cook', function(eggs, bacon, callback) {
assert.equal(1, eggs);
assert.equal(2, bacon);
callback();
});
hooks.execPost('cook', null, [1, 2], function(error, eggs, bacon) {
assert.ifError(error);
assert.equal(1, eggs);
assert.equal(2, bacon);
});
var execed = {};
hooks.post('cook', function(eggs, bacon) {
execed.first = true;
assert.equal(1, eggs);
assert.equal(2, bacon);
});
hooks.post('cook', function(eggs, bacon, callback) {
execed.second = true;
assert.equal(1, eggs);
assert.equal(2, bacon);
callback();
});
hooks.execPost('cook', null, [1, 2], function(error, eggs, bacon) {
assert.ifError(error);
assert.equal(2, Object.keys(execed).length);
assert.ok(execed.first);
assert.ok(execed.second);
assert.equal(1, eggs);
assert.equal(2, bacon);
});
You can also return a promise from your post hooks instead of calling
next()
. When the returned promise resolves, kareem will kick off the
next middleware.
hooks.post('cook', function(bacon) {
return new Promise(resolve => {
setTimeout(() => {
this.bacon = 3;
resolve();
}, 100);
});
});
var obj = { bacon: 0 };
hooks.execPost('cook', obj, obj, function() {
assert.equal(obj.bacon, 3);
});
acquit:ignore:end
hooks.pre('cook', true, function(next, done) {
this.bacon = 3;
next();
setTimeout(function() {
done();
}, 5);
});
hooks.pre('cook', true, function(next, done) {
next();
var _this = this;
setTimeout(function() {
_this.eggs = 4;
done();
}, 10);
});
hooks.pre('cook', function(next) {
this.waffles = false;
next();
});
hooks.post('cook', function(obj) {
obj.tofu = 'no';
});
var obj = { bacon: 0, eggs: 0 };
var args = [obj];
args.push(function(error, result) {
assert.ifError(error);
assert.equal(null, error);
assert.equal(3, obj.bacon);
assert.equal(4, obj.eggs);
assert.equal(false, obj.waffles);
assert.equal('no', obj.tofu);
assert.equal(obj, result);
});
hooks.wrap(
'cook',
function(o, callback) {
assert.equal(3, obj.bacon);
assert.equal(4, obj.eggs);
assert.equal(false, obj.waffles);
assert.equal(undefined, obj.tofu);
callback(null, o);
},
obj,
args);
hooks.pre('cook', true, function(next, done) {
this.bacon = 3;
next();
setTimeout(function() {
done();
}, 5);
});
hooks.pre('cook', true, function(next, done) {
next();
var _this = this;
setTimeout(function() {
_this.eggs = 4;
done();
}, 10);
});
hooks.pre('cook', function(next) {
this.waffles = false;
next();
});
hooks.post('cook', function(obj) {
obj.tofu = 'no';
});
var obj = { bacon: 0, eggs: 0 };
var cook = hooks.createWrapper(
'cook',
function(o, callback) {
assert.equal(3, obj.bacon);
assert.equal(4, obj.eggs);
assert.equal(false, obj.waffles);
assert.equal(undefined, obj.tofu);
callback(null, o);
},
obj);
cook(obj, function(error, result) {
assert.ifError(error);
assert.equal(3, obj.bacon);
assert.equal(4, obj.eggs);
assert.equal(false, obj.waffles);
assert.equal('no', obj.tofu);
assert.equal(obj, result);
});
acquit:ignore:end
var k1 = new Kareem();
k1.pre('cook', function() {});
k1.post('cook', function() {});
var k2 = k1.clone();
assert.deepEqual(Array.from(k2._pres.keys()), ['cook']);
assert.deepEqual(Array.from(k2._posts.keys()), ['cook']);
var k1 = new Kareem();
var test1 = function() {};
k1.pre('cook', test1);
k1.post('cook', function() {});
var k2 = new Kareem();
var test2 = function() {};
k2.pre('cook', test2);
var k3 = k2.merge(k1);
assert.equal(k3._pres.get('cook').length, 2);
assert.equal(k3._pres.get('cook')[0].fn, test2);
assert.equal(k3._pres.get('cook')[1].fn, test1);
assert.equal(k3._posts.get('cook').length, 1);
FAQs
Next-generation take on pre/post function hooks
The npm package kareem receives a total of 1,629,768 weekly downloads. As such, kareem popularity was classified as popular.
We found that kareem 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.
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.