Socket
Socket
Sign inDemoInstall

koa-actuator

Package Overview
Dependencies
2
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.3.0 to 0.4.0

106

lib/index.js
const compose = require('koa-compose');
const os = require('os');
const assert = require('assert');

@@ -8,6 +7,3 @@ const {loadPackageJson} = require('./utils');

const HEALTH_PATH = '/health';
const ENV_PATH = '/env';
const INFO_PATH = '/info';
const METRICS_PATH = '/metrics';
const SECURE_PROP_NAMES = ['admin', 'user', 'password', 'pass', 'pwd', 'login', 'username'];

@@ -21,9 +17,12 @@ /**

const timeout = options.checkTimeout || 5000;
const HEALTH_ENDPOINT = options.actuatorPath ? options.actuatorPath + HEALTH_PATH : '/actuator' + HEALTH_PATH;
return async function healthMiddleware(ctx, next) {
if (HEALTH_PATH === ctx.path) {
if (HEALTH_ENDPOINT === ctx.path) {
const health = {status: 'UP'};
if (Object.keys(checks).length > 0) health.details = {};
for (const checkName of Object.keys(checks)) {
const checkResult = await runCheck(checks[checkName], timeout);
health[checkName] = checkResult;
const checkResult = wrapDetails(await runCheck(checks[checkName], timeout));
health.details[checkName] = checkResult;
if (checkResult && checkResult.status === 'DOWN') {

@@ -38,2 +37,15 @@ health.status = checkResult.status;

}
};
function wrapDetails(checkResult) {
const wrappedResult = {};
for(const field of Object.keys(checkResult)) {
if (field === 'status') {
wrappedResult[field] = checkResult[field];
} else {
wrappedResult.details = wrappedResult.details || {};
wrappedResult.details[field] = checkResult[field];
}
}
return wrappedResult;
}

@@ -47,3 +59,3 @@ }

new Promise((resolve, reject) => setTimeout(() => reject(new Error('Check timed out')), timeout))
]);
]);
} catch (e) {

@@ -57,62 +69,24 @@ return {status: 'DOWN', error: e && (e.message || e.toString())};

*/
async function info(ctx, next) {
if (INFO_PATH === ctx.path) {
if (package_json) {
ctx.body = {
build:
{
version: package_json.version,
name: package_json.name,
main: package_json.main,
description: package_json.description
}
};
} else {
ctx.body = {};
}
} else {
await next();
}
}
function info(options) {
/**
* Exposes environment properties. Secure variables (such as 'user', 'password', 'pass' etc are) values will be hidden.
*/
async function env(ctx, next) {
if (ENV_PATH === ctx.path) {
const envCopy = Object.assign({}, process.env);
Object
.keys(envCopy)
.filter(property => {
const propLowerCase = property.toLowerCase();
return SECURE_PROP_NAMES.some(p => propLowerCase.includes(p));
})
.forEach(property => envCopy[property] = '*******'); //hide secure details
ctx.body = {systemEnvironment: envCopy, arguments: process.argv};
} else {
await next();
}
}
const INFO_ENDPOINT = options.actuatorPath ? options.actuatorPath + INFO_PATH : '/actuator' + INFO_PATH;
/**
* Exposes application and resources information. E.g. name, version, memory and CPU usage
*/
async function metrics(ctx, next) {
if (METRICS_PATH === ctx.path) {
const memory = process.memoryUsage();
ctx.body = {
timestamp: Date.now(),
uptime: process.uptime(),
processors: os.cpus().length,
heap: memory.heapTotal,
'heap.used': memory.heapUsed,
resources: {
memory: memory,
loadavg: os.loadavg(),
cpu: JSON.stringify(os.cpus()),
nics: JSON.stringify(os.networkInterfaces())
return async function infoMiddleware(ctx, next) {
if (INFO_ENDPOINT === ctx.path) {
if (package_json) {
ctx.body = {
build:
{
version: package_json.version,
name: package_json.name,
main: package_json.main,
description: package_json.description
}
};
} else {
ctx.body = {};
}
};
} else {
await next();
} else {
await next();
}
}

@@ -122,3 +96,3 @@ }

module.exports = (healthChecks = {}, options = {}) => {
return compose([health(healthChecks, options), env, info, metrics]);
return compose([health(healthChecks, options), info(options)]);
};
{
"name": "koa-actuator",
"version": "0.3.0",
"version": "0.4.0",
"description": "Healthcheck and monitoring middleware for koa inspired by java spring's actuators",

@@ -5,0 +5,0 @@ "main": "lib/index.js",

@@ -18,3 +18,3 @@ const actuator = require('../lib/index');

request(app.callback())
.get('/health')
.get('/actuator/health')
.expect(200)

@@ -24,2 +24,15 @@ .expect({status: 'UP'}, done);

it('should use parameter actuatorPath if it is defined', (done) => {
const options = {actuatorPath: '/custom-actuator-path'};
const app = new Koa();
app.use(actuator({}, options));
request(app.callback())
.get('/custom-actuator-path/health')
.expect(200)
.expect({status: 'UP'}, done);
});
it('should return 200 and status UP if sync and async checks pass', (done) => {

@@ -35,8 +48,10 @@ //arrange

request(app.callback())
.get('/health')
.get('/actuator/health')
.expect(200)
.expect({
status: 'UP',
db: {status: 'UP', freeConnections: 10},
redis: {status: 'UP', usedMemory: '52m', uptimeDays: 16}
details: {
db: {status: 'UP', details: {freeConnections: 10}},
redis: {status: 'UP', details: {usedMemory: '52m', uptimeDays: 16}}
}
}, done);

@@ -55,8 +70,10 @@ });

request(app.callback())
.get('/health')
.get('/actuator/health')
.expect(503)
.expect({
status: 'DOWN',
db: {status: 'UP', freeConnections: 10},
redis: {status: 'DOWN', usedMemory: '52m', uptimeDays: 16}
details: {
db: {status: 'UP', details: {freeConnections: 10}},
redis: {status: 'DOWN', details:{usedMemory: '52m', uptimeDays: 16}}
}
}, done);

@@ -69,3 +86,5 @@ });

app.use(actuator({
db: () => { throw new Error('unexpected error'); },
db: () => {
throw new Error('unexpected error');
},
redis: () => Promise.resolve({status: 'UP', usedMemory: '52m', uptimeDays: 16})

@@ -76,8 +95,10 @@ }));

request(app.callback())
.get('/health')
.get('/actuator/health')
.expect(503)
.expect({
status: 'DOWN',
db: {status: 'DOWN', error: 'unexpected error'},
redis: {status: 'UP', usedMemory: '52m', uptimeDays: 16}
details: {
db: {status: 'DOWN', details: {error: 'unexpected error'}},
redis: {status: 'UP', details: {usedMemory: '52m', uptimeDays: 16}}
}
}, done);

@@ -96,8 +117,10 @@ });

request(app.callback())
.get('/health')
.get('/actuator/health')
.expect(503)
.expect({
status: 'DOWN',
db: {status: 'UP', freeConnections: 10},
redis: {status: 'DOWN', error: 'unexpected async error'}
details: {
db: {status: 'UP', details: {freeConnections: 10}},
redis: {status: 'DOWN', details: {error: 'unexpected async error'}}
}
}, done);

@@ -116,7 +139,9 @@ });

request(app.callback())
.get('/health')
.get('/actuator/health')
.expect(503)
.expect({
status: 'DOWN',
db: {status: 'DOWN', error: 'Check timed out'},
details: {
db: {status: 'DOWN', details: {error: 'Check timed out'}}
}
}, done);

@@ -144,3 +169,3 @@ });

request(app.callback())
.get('/info')
.get('/actuator/info')
.expect(200)

@@ -154,46 +179,27 @@ .end((err, res) => {

it('should return 200 and empty object if package.json not found', (done) => {
it('should use parameter actuatorPath if it is defined', (done) => {
//arrange
sinon.stub(utils, 'loadPackageJson').returns(null);
delete require.cache[require.resolve('../lib/index')];
const actuator = require('../lib/index');
const options = {actuatorPath: '/custom-actuator-path'};
const app = new Koa();
app.use(actuator());
app.use(actuator({}, options));
//act & assert
request(app.callback())
.get('/info')
.get('/custom-actuator-path/info')
.expect(200)
.end((err, res) => {
if (err) return done(err);
assert.deepEqual(res.body, {});
assert.isDefined(res.body.build.version);
done();
});
});
});
describe('/env', () => {
it('should return 200 and the list of environment variables', (done) => {
it('should return 200 and empty object if package.json not found', (done) => {
//arrange
process.env.TEST_VAR = 'test';
const app = new Koa();
app.use(actuator());
sinon.stub(utils, 'loadPackageJson').returns(null);
delete require.cache[require.resolve('../lib/index')];
const actuator = require('../lib/index');
//act & assert
request(app.callback())
.get('/env')
.expect(200)
.end((err, res) => {
if (err) return done(err);
assert.equal(res.body.systemEnvironment.TEST_VAR, 'test');
done();
});
});
it('should hide secure data', (done) => {
//arrange
process.env.TEST_VAR = 'test';
process.env.USERNAME = 'test-username';
process.env.PASSWORD = 'test-password';
const app = new Koa();

@@ -204,9 +210,7 @@ app.use(actuator());

request(app.callback())
.get('/env')
.get('/actuator/info')
.expect(200)
.end((err, res) => {
if (err) return done(err);
assert.equal(res.body.systemEnvironment.TEST_VAR, 'test');
assert.equal(res.body.systemEnvironment.USERNAME, '*******');
assert.equal(res.body.systemEnvironment.PASSWORD, '*******');
assert.deepEqual(res.body, {});
done();

@@ -217,23 +221,2 @@ });

describe('/metrics', () => {
it('should return 200 and show some service info (like uptime, heap usage etc)', (done) => {
//arrange
const app = new Koa();
app.use(actuator());
//act & assert
request(app.callback())
.get('/metrics')
.expect(200)
.end((err, res) => {
if (err) return done(err);
assert.property(res.body, 'uptime');
assert.property(res.body, 'processors');
assert.property(res.body, 'heap');
assert.property(res.body, 'heap.used');
assert.deepProperty(res.body, 'resources.memory');
done();
});
});
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc