jesusprubio / shodan-client

:eyes: Node.js/JavaScript Library for accessing the new Shodan API
MIT License
198 stars 66 forks source link

count() should return facets #34

Closed noraj closed 4 years ago

noraj commented 4 years ago

Describe the bug

count() should return an object with { matches: [], factes: {}, total: int }, for example:

{
 "matches": [ ],
 "facets": {
  "org": [
   {
    "count": 286,
    "value": "Korea Telecom"
   },
   {
    "count": 229,
    "value": "Comcast Cable"
   },
   {
    "count": 113,
    "value": "Taiwan Academic Network"
   },
   {
    "count": 107,
    "value": "University of Minnesota"
   },
   {
    "count": 104,
    "value": "Tiawan Academic Network (TANet) Information Center"
   }
  ]
 },
 "total": 12039
}

But currently it is only returning matches and total but not facets.

See the official documentation https://developer.shodan.io/api

To Reproduce

const util = require('util');
const api = require('shodan-client');

const key = 'xxx';

const FACETS = {
  'org': 3,
  'domain': 5,
  'port': 5,
  'asn': 5,
  'country': 10,
};

const FACET_TITLES = {
  'org': 'Top 3 Organizations',
  'domain': 'Top 5 Domains',
  'port': 'Top 5 Ports',
  'asn': 'Top 5 Autonomous Systems',
  'country': 'Top 10 Countries',
};

const opts = { facets: FACETS };

// Query
const query = 'apache 2.4';

api
  .count(query, key, opts) // Count results
  .then(result => {
    console.log('Shodan Summary Information');
    console.log(`Query: ${query}`);
    console.log(`Total Results: ${result['total']}\n`);

    // Print the summary info from the facets
    console.log(util.inspect(result, { depth: 6 }));
    //for (const facet in result['facets']) {
    //  console.log(FACET_TITLES[facet]);
    //}
  })
  .catch(err => {
    console.log('Error:');
    console.log(err);
  });

Ouput:

Shodan Summary Information
Query: apache 2.4
Total Results: 66069

{ matches: [], total: 66069 }

Expected behavior

Returns facets.

Environment

noraj commented 4 years ago

It must be because facets option is taking a string as an argument instead of a dictionary:

eg

const searchOpts = {
  facets: 'port:100,country:100',
  // minify: false,
};

instead of:

const searchOpts = {
  facets: {
    'port': 100,
    'country': 100
  }
  // minify: false,
};

or

const searchOpts = {
  facets: {
    port: 100,
    country: 100
  }
  // minify: false,
};
noraj commented 4 years ago

@jesusprubio What do you think about it?

noraj commented 4 years ago

Workaround

- const opts = { facets: FACETS };
+ const opts = { facets: JSON.stringify(FACETS).replace(/["{}]/g, '') };
jesusprubio commented 4 years ago

Thanks for the report. This is not an error, it respects the API expected format for the parameters.

I think it has sense since the HTTP API expects a JSON, which is the same data structure we use in Node.js. Moreover, the count part is optional, so we should accept null or undefined as a valid value, which is a bit strange. We could use an array instead, but it would be still stranger if you think in what we should allow as valid elements.