Skip to content

Commit

Permalink
Merge 7624aa2 into 0e190d2
Browse files Browse the repository at this point in the history
  • Loading branch information
ajvb committed May 4, 2020
2 parents 0e190d2 + 7624aa2 commit ec55277
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 33 deletions.
142 changes: 113 additions & 29 deletions lib/client.js
Expand Up @@ -25,6 +25,24 @@ var ipSchema = Joi.string().ip({
cidr: false
});

var emailSchema = Joi.string().email();

var validate = function(type, obj) {
if (type === 'ip') {
if (Joi.validate(obj, ipSchema).error !== null) {
return new Error('Invalid IP.');
}
} else if (type === 'email') {
if (Joi.validate(obj, emailSchema).error !== null) {
return new Error('Invalid Email.');
}
} else {
return new Error('Invalid type: '+type);
}
return null;
};


/**
* @class IPReputationServiceClient
* @constructor
Expand Down Expand Up @@ -64,55 +82,73 @@ var client = function(config) {
};

/**
* @method get
* @param {String} ip a Joi strip.ip IP address to fetch reputation for
* @method getTyped
* @param {String} type Object type to get reputation for (ip or email)
* @param {String} obj Object to get reputation for
* @return {Promise}
*/
client.prototype.get = function (ip) {
if (Joi.validate(ip, ipSchema).error !== null) {
return Promise.reject(new Error('Invalid IP.'));
client.prototype.getTyped = function (type, obj) {
if (validate(type, obj) !== null) {
return Promise.reject(validate(type, obj));
}
return this.baseRequest({
method: 'GET',
uri: '/' + ip,
uri: '/type/' + type + '/' + obj,
hawk: {
contentType: null
}
}).timeout(this.timeout);
}).timeout(this.timeout).then(function (response) {
if (response && response.body && response.statusCode) {
if (type === 'ip') {
response.body.ip = obj;
}
}
return response;
});
};

/**
* @method update
* @param {String} ip an IP address to change a reputation for
* @method updateTyped
* @param {String} type Object type to update the reputation for (ip or email)
* @param {String} obj Object to update the reputation for
* @param {Number} reputation a reputation/trust value from 0 to 100 inclusive (higher is more trustworthy)
* @return {Promise}
*/
client.prototype.update = function (ip, reputation) {
if (Joi.validate(ip, ipSchema).error !== null) {
return Promise.reject(new Error('Invalid IP.'));
client.prototype.updateTyped = function (type, obj, reputation) {
if (validate(type, obj) !== null) {
return Promise.reject(validate(type, obj));
}
return this.baseRequest({
method: 'PUT',
uri: '/' + ip,
uri: '/type/' + type + '/' + obj,
hawk: {
payload: JSON.stringify({'reputation': reputation})
payload: JSON.stringify({
'reputation': reputation,
'object': obj,
'type': type
})
},
json: {'reputation': reputation}
json: {
'reputation': reputation,
'object': obj,
'type': type
}
}).timeout(this.timeout);
};

/**
* @method remove
* @param {String} ip an IP address to remove an associated reputation for
* @method removeTyped
* @param {String} type Object type to remove an associated reputation for (ip or email)
* @param {String} obj Object to remove an associated reputation for
* @return {Promise}
*/
client.prototype.remove = function (ip) {
if (Joi.validate(ip, ipSchema).error !== null) {
return Promise.reject(new Error('Invalid IP.'));
client.prototype.removeTyped = function (type, obj) {
if (validate(type, obj) !== null) {
return Promise.reject(validate(type, obj));
}
return this.baseRequest({
method: 'DELETE',
uri: '/' + ip,
uri: '/type/' + type + '/' + obj,
hawk: {
payload: '', // force sending a hawk hash that reputation service requires
contentType: null
Expand All @@ -121,23 +157,71 @@ client.prototype.remove = function (ip) {
};

/**
* @method sendViolation
* @param {String} ip an IP address to record a violation for
* @method sendViolationTyped
* @param {String} type Object type to record a violation for
* @param {String} obj Object to record a violation for
* @param {String} violationType an violation type to save lookup the reputation penalty for
* @return {Promise}
*/
client.prototype.sendViolation = function (ip, violationType) {
if (Joi.validate(ip, ipSchema).error !== null) {
return Promise.reject(new Error('Invalid IP.'));
client.prototype.sendViolationTyped = function (type, obj, violationType) {
if (validate(type, obj) !== null) {
return Promise.reject(validate(type, obj));
}
return this.baseRequest({
method: 'PUT',
uri: '/violations/' + ip,
uri: '/violations/type/' + type + '/' + obj,
hawk: {
payload: JSON.stringify({'ip': ip, 'violation': violationType})
payload: JSON.stringify({
'violation': violationType,
'type': type,
'object': obj
})
},
json: {'ip': ip, 'violation': violationType}
json: {
'violation': violationType,
'type': type,
'object': obj,
}
}).timeout(this.timeout);
};


/**
* @method get
* @param {String} ip a Joi strip.ip IP address to fetch reputation for
* @return {Promise}
*/
client.prototype.get = function (ip) {
return this.getTyped('ip', ip);
};

/**
* @method update
* @param {String} ip an IP address to change a reputation for
* @param {Number} reputation a reputation/trust value from 0 to 100 inclusive (higher is more trustworthy)
* @return {Promise}
*/
client.prototype.update = function (ip, reputation) {
return this.updateTyped('ip', ip, reputation);
};

/**
* @method remove
* @param {String} ip an IP address to remove an associated reputation for
* @return {Promise}
*/
client.prototype.remove = function (ip) {
return this.removeTyped('ip', ip);
};

/**
* @method sendViolation
* @param {String} ip an IP address to record a violation for
* @param {String} violationType an violation type to save lookup the reputation penalty for
* @return {Promise}
*/
client.prototype.sendViolation = function (ip, violationType) {
return this.sendViolationTyped('ip', ip, violationType);
};

module.exports = client;
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

91 changes: 90 additions & 1 deletion test/local/reputation_service_client_tests.js
Expand Up @@ -13,6 +13,7 @@ var client = new IPReputationClient({
timeout: 150
});
var invalidIPError = new Error('Invalid IP.');
var invalidEmailError = new Error('Invalid Email.');

// Tests in this file are specifically ordered

Expand Down Expand Up @@ -68,6 +69,32 @@ test(
}
);

test(
'does not get reputation for a nonexistent email',
function (t) {
client.getTyped('email', 'not-there@example.com').then(function (response) {
t.equal(response.statusCode, 404);
t.end();
});
}
);

test(
'does not get reputation for a invalid type',
function (t) {
t.rejects(client.getTyped('not-real', 'foobar'), new Error('Invalid type: not-real'));
t.end();
}
);

test(
'does not get reputation for a invalid email',
function (t) {
t.rejects(client.getTyped('email', 'not-an-email'), invalidEmailError);
t.end();
}
);

test(
'does not get reputation for a invalid IP',
function (t) {
Expand Down Expand Up @@ -100,6 +127,26 @@ test(
}
);

test(
'update reputation for new email',
function (t) {
client.updateTyped('email', 'picard@example.com', 50).then(function (response) {
t.equal(response.statusCode, 200);
t.end();
});
}
);

test(
'update reputation for existing email',
function (t) {
client.updateTyped('email', 'picard@example.com', 57).then(function (response) {
t.equal(response.statusCode, 200);
t.end();
});
}
);

test(
'update reputation for new IP',
function (t) {
Expand Down Expand Up @@ -133,7 +180,21 @@ test(
);

test(
'gets reputation for a existing IP',
'gets reputation for an existing IP (typed)',
function (t) {
client.getTyped('ip', '127.0.0.1').then(function (response) {
t.equal(response.statusCode, 200);
t.equal(response.body.reputation, 75);
t.equal(response.body.object, '127.0.0.1');
t.equal(response.body.reviewed, false);
// also response.body.lastupdated, which is dynamic (time of previous test request)
t.end();
});
}
);

test(
'gets reputation for an existing IP',
function (t) {
client.get('127.0.0.1').then(function (response) {
t.equal(response.statusCode, 200);
Expand All @@ -146,6 +207,34 @@ test(
}
);

test(
'gets reputation for an existing email',
function (t) {
client.getTyped('email', 'picard@example.com').then(function (response) {
t.equal(response.statusCode, 200);
t.equal(response.body.reputation, 57);
t.equal(response.body.object, 'picard@example.com');
t.equal(response.body.reviewed, false);
t.end();
});
}
);

test(
'removes reputation for existing email',
function (t) {
client.removeTyped('email', 'picard@example.com').then(function (response) {
t.equal(response.statusCode, 200);
t.equal(response.body, undefined);
return client.getTyped('email', 'picard@example.com');
}).then(function (response) {
t.equal(response.statusCode, 404);
t.equal(response.body, undefined); // JSON.stringify() -> undefined
t.end();
});
}
);

test(
'removes reputation for existing IP',
function (t) {
Expand Down

0 comments on commit ec55277

Please sign in to comment.