Git Product home page Git Product logo

enzoic-javascript-client's Introduction

Enzoic JavaScript Client Library

โš ๏ธ In version 3.0.0, the Enzoic JavaScript library has switched to using promises rather than callbacks. This is a breaking change and will require updates to code that used previous versions of this library.

TOC

This README covers the following topics:

Installation

$ npm install @enzoic/enzoic

API Overview

Below is some simple example code which demonstrates the usage of the API.

const Enzoic = require('enzoic');

// Create a new Enzoic instance - this is our primary interface for making API calls
const enzoic = new Enzoic(YOUR_API_KEY, YOUR_API_SECRET);

// Check whether a specific set of credentials are compromised
const credsCompromised = await enzoic.checkCredentials('[email protected]', 'password-to-test'); 

if (credsCompromised === true) {
    console.log('Credentials are compromised');
}
else {
    console.log('Credentials are not compromised');
}

More information in reference format can be found below.

The Enzoic constructor

The first step to use the API is to instantiate the Enzoic Client with the API key and secret you were issued on Enzoic signup.

const Enzoic = require('enzoic');

const enzoic = new Enzoic(YOUR_API_KEY, YOUR_API_SECRET);

If you were instructed to use an alternate API host, you may call the overloaded constructor and pass the host you were provided.

const Enzoic = require('enzoic');

const enzoic = new Enzoic(YOUR_API_KEY, YOUR_API_SECRET, "api-alt.enzoic.com");

Passwords API Examples

See https://docs.enzoic.com/enzoic-api-developer-documentation/api-reference/passwords-api

// Check whether a password has been compromised
const passwordCompromised = await enzoic.checkPassword('password-to-test');

if (passwordCompromised === true) {
    console.log('Password is compromised');
}
else {
    console.log('Password is not compromised');
}

Credentials API Examples

See https://docs.enzoic.com/enzoic-api-developer-documentation/api-reference/credentials-api

// Check whether a specific set of credentials are compromised
const credsCompromised = await enzoic.checkCredentials('[email protected]', 'password-to-test'); 

if (credsCompromised === true) {
    console.log('Credentials are compromised');
}
else {
    console.log('Credentials are not compromised');
}

// Enhanced version of checkCredentials offering more control over performance.
// The call introduces an options object parameter, which supports the following settings:
//
// lastCheckDate: 
// The timestamp for the last check you performed for this user.
// If the date/time you provide for the last check is greater than the timestamp Enzoic has for the last
// breach affecting this user, the check will not be performed.  This can be used to substantially increase performance.
//
// excludeHashAlgorithms: 
// An array of PasswordTypes to ignore when calculating hashes for the credentials check.   
// By excluding computationally expensive PasswordTypes, such as BCrypt, it is possible to balance the performance of this
// call against security.
//
const credsCompromised = await enzoic.checkCredentialsEx('[email protected]', 'password-to-test', 
    {
        lastCheckDate: new Date('2016-12-10T02:05:03.000Z'), 
        excludeHashAlgorithms: [8, 11, 12] // see https://docs.enzoic.com/enzoic-api-developer-documentation/api-reference/password-hash-algorithms 
    });

if (credsCompromised === true) {
    console.log('Credentials are compromised');
}
else {
    console.log('Credentials are not compromised');
}

// get all passwords Enzoic has for the specified user 
// returns results per 
// https://docs.enzoic.com/enzoic-api-developer-documentation/api-reference/credentials-api/cleartext-credentials-api
const userPasswordsResponse = await enzoic.getUserPasswords("[email protected]");

// print user passwords
for (let i = 0; i < userPasswordsResponse.passwords.length; i++) {
    console.log('Password: ' + userPasswordsResponse.passwords[i].Password + '\n');
}

Exposures API Examples

See https://docs.enzoic.com/enzoic-api-developer-documentation/api-reference/exposures-api

// get all exposures for the given user
const exposuresForUser = await enzoic.getExposuresForUser('[email protected]');
console.log(exposuresForUser.exposures.count + ' exposures found for [email protected]');
    
// now get the full details for the first exposure returned in the list
const exposureDetails = await enzoic.getExposureDetails(result.exposures[0]);
console.log('First exposure for [email protected] was ' + exposureDetails.title);

// get all exposures for a given domain - second parameter indicates whether to include exposure details in results
// returns paged results per 
// https://docs.enzoic.com/enzoic-api-developer-documentation/api-reference/exposures-api/get-exposures-for-a-domain
const exposuresForDomain = enzoic.getExposuresForDomainEx('enzoic.com', true, 20, null);
console.log(exposuresForDomain.count + ' exposures found for enzoic.com');

// print first page of results
for (let i = 0; i < exposuresForDomain.exposures.length; i++) {
    console.log('Exposure: ' + exposuresForDomain.exposures.title + '\n');
}

// if pagingToken present, get next page of results
if (exposuresForDomain.pagingToken) {
    const secondPageResults = await enzoic.getExposuresForDomainEx('enzoic.com', true, 20, exposuresForDomain.pagingToken);
    // ...process second page of results, etc.
}

// get all users exposed for a given domain
// returns paged results per 
// https://docs.enzoic.com/enzoic-api-developer-documentation/api-reference/exposures-api/get-exposures-for-all-email-addresses-in-a-domain
const exposedUsers = await enzoic.getExposedUsersForDomain('enzoic.com', 20, null);

// print first page of results
for (let i = 0; i < exposedUsers.users.length; i++) {
   console.log('Exposed User: ' + exposedUsers.users[i].username + '\n');
}

// if pagingToken present, get next page of results
if (exposedUsers.pagingToken) {
    const secondPageResults = await enzoic.getExposedUsersForDomain('enzoic.com', 20, exposedUsers.pagingToken); 
    // ...process second page of results, etc.
}

Breach Monitoring by User API Examples

See https://docs.enzoic.com/enzoic-api-developer-documentation/api-reference/breach-monitoring-api/breach-monitoring-by-user

// a couple of email addresses - note: these will get hashed before submission to the Enzoic API
const arrUsernames = [
    '[email protected]', 
    '[email protected]'
];

// subscribe for alerts for these users
const addResponse = await enzoic.addUserAlertSubscriptions(arrUsernames);

console.log('New subscriptions added: ' + addResponse.added + '\n' + 
    'Subscriptions already existing: ' + addResponse.alreadyExisted);

// delete subscriptions for these users
const deleteResponse = await enzoic.deleteUserAlertSubscriptions(arrUsernames);

console.log('Subscriptions deleted: ' + deleteResponse.deleted + '\n' + 
    'Subscriptions not found: ' + deleteResponse.notFound);

// check whether a user is already subscribed
const subscribed = await enzoic.isUserSubscribedForAlerts(arrUsernames[0]);

if (subscribed === true) {
   console.log('User already subscribed');
}
else {
   console.log('User not already subscribed');
}    

// get all users subscribed for alerts on this account 
// returns paged results per 
// https://docs.enzoic.com/enzoic-api-developer-documentation/api-reference/breach-monitoring-api/breach-monitoring-by-user#retrieve-current-breach-alert-subscriptions
const subscriptionsResponse = await enzoic.getUserAlertSubscriptions(4 /* page size */, null /* paging token - null on first call */);

// print first page of results
for (let i = 0; i < subscriptionsResponse.usernameHashes.length; i++) {
   console.log('Username Hash: ' + subscriptionsResponse.usernameHashes[i] + '\n');
}

// if pagingToken present, get next page of results
if (subscriptionsResponse.pagingToken) {
    const secondPageResponse = await enzoic.getUserAlertSubscriptions(4, subscriptionsResponse.pagingToken);
    // ...process second page of results, etc.
}

Breach Monitoring by Domain API Examples

See https://docs.enzoic.com/enzoic-api-developer-documentation/api-reference/breach-monitoring-api/breach-monitoring-by-domain

// test domains for alert subscriptions
const arrDomains = [
    'testdomain1.com', 
    'testdomain2.com' 
];

// subscribe for alerts for these domains
const addDomainsResponse = await enzoic.addDomainAlertSubscriptions(arrDomains); 

console.log('New subscriptions added: ' + addDomainsResponse.added + '\n' + 
    'Subscriptions already existing: ' + addDomainsResponse.alreadyExisted);

// delete subscriptions for these domains
const deleteDomainsResponse = await enzoic.deleteDomainAlertSubscriptions(arrDomains);

console.log('Subscriptions deleted: ' + deleteDomainsResponse.deleted + '\n' + 
    'Subscriptions not found: ' + deleteDomainsResponse.notFound);

// check whether a domain is already subscribed
const domainSubscribed = await enzoic.isDomainSubscribedForAlerts(arrDomains[0]); 

if (subscribed === true) {
   console.log('Domain already subscribed');
}
else {
   console.log('Domain not already subscribed');
}    

// get all users subscribed for alerts on this account 
// returns pages results per 
// https://docs.enzoic.com/enzoic-api-developer-documentation/api-reference/breach-monitoring-api/breach-monitoring-by-domain#retrieve-current-breach-alert-subscriptions
const domainSubsResponse = await enzoic.getDomainAlertSubscriptions(4 /* page size */, null /* paging token - null on first call */);

// print first page of results
for (let i = 0; i < domainSubsResponse.domains.length; i++) {
   console.log('Domain: ' + domainSubsResponse.domains[i] + '\n');
}

// if pagingToken present, get next page of results
if (domainSubsResponse.pagingToken) {
    const secondPageResponse = await enzoic.getDomainAlertSubscriptions(4, domainSubsResponse.pagingToken);
    // ...process second page of results, etc.
}

enzoic-javascript-client's People

Stargazers

Patrick Sprowls avatar SAldous avatar Waren Gonzaga avatar Mike Wilson avatar

Watchers

James Cloos avatar Mike Wilson avatar

enzoic-javascript-client's Issues

Hashing functions output clarification

Hi!

Could you please elaborate on the sha256crypt hashing functions output format?
The API docs don't specify the used format of these types of hashes and the ruby client seems to be doing it differently. Do you only use the default 5000 rounds of calculation with the sha256crypt hashes? Do you ever include the number of rounds in the output format?

I gathered the following:
The JavaScript client implementation for sha256crypt uses unixcrypt and it does not cut the salt (see below for sha512crypt) and is working correctly with the different salt formats:

  • if the number of rounds is omitted the output will omit it
  • if the number of rounds is specified in the salt, the output will contain it

The Ruby client implementation produces the output without the number of rounds part (rounds=<N>$) even if the input salt contains it. Evidence in the testing file.
Comparing that to the same test with the same inputs in the JavaScript implementation, we see that they expect different outputs, the JS one with the number of rounds part and the Ruby one without the number of rounds part.

So the questions again: Do you only use the default 5000 rounds of calculation with the sha256crypt hashes? Do you ever include the number of rounds in the output format?

Another observation:
The JavaScript client implementation uses sha512crypt-node for sha512crypt type hashes. The sha512Crypt function here takes the sSalt parameter, cuts out the magic $6$ value at the beginning (with sSalt.substring(3)) of the string and passes it to the used library's exported b64_sha512crypt function. This will call the b64_sha512crypt hash function which will use the default 5000 rounds and it will produce the following format $6$<salt>$<digest> where <salt> does not contain the rounds=<N>$ part. This is all good when using a salt value like '$6$testsalt'.

But the used library allows (as does the standard) the salt to describe the number of rounds.
When we give the enzoic sha512Crypt function a salt value where the number of rounds part is used (like '$6$rounds=4999$testsalt'), the sha512crypt-node module's function will throw an error, because the enzoic function cut off the magic $6$ before passing it (the length of the magic_array will be bigger than 1, but the checked item won't be equal to 6):

Error: Got 'rounds=5000$52450745' but only SHA512 ($6$) algorithm supported
at sha512crypt (node_modules/sha512crypt-node/sha512crypt.js:133:19)
at Object.sha512Crypt (src/hashing.js:261:16)
at Context.<anonymous> (test/hashing.js:210:28)
at process.processImmediate (node:internal/timers:478:21)

Wouldn't it be better if it did not cut the salt at the magic id value and let the underlying library do the parsing of the salt?

Please add missing endpoint for getExposuresForDomain

Please add to /passwordping.js:

`PasswordPing.prototype.getExposuresForDomain = function (sDomain, iPageSize, sPagingToken, fnCallback) {
var path = '/v1/exposures';

var queryString = 'accountDomain=' + sDomain;

if (iPageSize) {
    queryString += '&pageSize=' + iPageSize;
}

if (sPagingToken) {
    queryString += '&pagingToken=' + sPagingToken;
}

this.makeRestCall(path, queryString, 'GET', null, function (err, response) {
    if (err) {
        fnCallback(err, null);
    }
    else if (response === 404) {
        // don't have this email in the DB - return empty response
        fnCallback(null, {
            count: 0,
            exposures: []
        });
    }
    else {
        fnCallback(null, response);
    }

});

};`

makeRestCall POST/PUT/DELETE missing content-type:application/json

The PasswordPing.prototype.makeRestCall() method does not allow for POST/PUT/DELETE methods since the API requires the request.headers['content-type'] to be set. Trying to POST data to the API using this method results in the following error:
{ "message": "Unexpected error from PasswordPing API", "error": "400 Bad Request" }

DELETE method requires request.headers['content-length'], otherwise 400 bad request.

Also, additional response codes would need to be handled for POST/PUT methods (201 specifically)

Error on Unix systems (nodejs): Error: Cannot find module './src/passwordtype'

Case sensitive include needed for nodejs on Unix based systems. Currently error is thrown on Unix platforms when starting node service:

/passwordping.js line 5:

var PasswordType = require('./src/passwordtype');
should be changed to:
var PasswordType = require('./src/passwordType');

Filename in package is ./src/passwordType.js

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.