From 9b01607240aa442b0a62a29182138338efdfc288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marta=20G=C3=B3mez=20Mac=C3=ADas?= Date: Fri, 11 May 2018 20:38:28 +0200 Subject: [PATCH] Add GET/rules/gdpr API call (#78) * Add GET/rules/gdpr API call * Update changelog for #78 * Add mocha tests for GDPR API calls --- CHANGELOG.md | 9 ++- controllers/rules.js | 49 ++++++++++++++- models/wazuh-api.py | 2 +- test/test_rules.js | 144 ++++++++++++++++++++++++++++++++++++++----- 4 files changed, 185 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce3002a6..5fec9784 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,15 @@ # Change Log All notable changes to this project will be documented in this file. +## [v3.2.3] +### Added +- Added a new API call `GET/rules/gdpr` ([#78](https://github.com/wazuh/wazuh-api/pull/78)). +- Added a parameter in `GET/rules` API call to filter by GDPR requirements ([#78](https://github.com/wazuh/wazuh-api/pull/78)). + ## [v3.2.2] -## Added +### Added - Added an option in `config.js` to run the API with root privileges for debug purposes and troubleshooting. The API runs as ossec by default. ([#68](https://github.com/wazuh/wazuh-api/pull/68)) -## Changed +### Changed - Changed mode from 750 to 660 in `/configuration/auth/user` file after installing it. diff --git a/controllers/rules.js b/controllers/rules.js index 5770f785..a82a0225 100755 --- a/controllers/rules.js +++ b/controllers/rules.js @@ -40,7 +40,11 @@ router.get('/', cache(), function(req, res) { req.apicacheGroup = "rules"; var data_request = {'function': '/rules', 'arguments': {}}; - var filters = {'offset': 'numbers', 'limit': 'numbers', 'sort':'sort_param', 'search':'search_param', 'status':'alphanumeric_param', 'group':'alphanumeric_param', 'level':'ranges', 'path':'paths', 'file':'alphanumeric_param', 'pci':'alphanumeric_param'}; + var filters = {'offset': 'numbers', 'limit': 'numbers', 'sort':'sort_param', + 'search':'search_param', 'status':'alphanumeric_param', + 'group':'alphanumeric_param', 'level':'ranges', 'path':'paths', + 'file':'alphanumeric_param', 'pci':'alphanumeric_param', + 'gdpr': 'alphanumeric_param'}; if (!filter.check(req.query, filters, req, res)) // Filter with error return; @@ -65,6 +69,8 @@ router.get('/', cache(), function(req, res) { data_request['arguments']['file'] = req.query.file; if ('pci' in req.query) data_request['arguments']['pci'] = req.query.pci; + if ('gdpr' in req.query) + data_request['arguments']['gdpr'] = req.query.gdpr; execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); }) @@ -147,6 +153,47 @@ router.get('/pci', cache(), function(req, res) { execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); }) + +/** + * @api {get} /rules/gdpr Get rule gdpr requirements + * @apiName GetRulesGdpr + * @apiGroup Info + * + * @apiParam {Number} [offset] First element to return in the collection. + * @apiParam {Number} [limit=500] Maximum number of elements to return. + * @apiParam {String} [sort] Sorts the collection by a field or fields (separated by comma). Use +/- at the beginning to list in ascending or descending order. + * @apiParam {String} [search] Looks for elements with the specified string. + * + * @apiDescription Returns the GDPR requirements of all rules. + * + * @apiExample {curl} Example usage: + * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/rules/gdpr?offset=0&limit=10&pretty" + * + */ +router.get('/gdpr', cache(), function(req, res) { + logger.debug(req.connection.remoteAddress + " GET /rules/gdpr"); + + req.apicacheGroup = "rules"; + + var data_request = {'function': '/rules/gdpr', 'arguments': {}}; + var filters = {'offset': 'numbers', 'limit': 'numbers', 'sort':'sort_param', 'search':'search_param'}; + + if (!filter.check(req.query, filters, req, res)) // Filter with error + return; + + if ('offset' in req.query) + data_request['arguments']['offset'] = req.query.offset; + if ('limit' in req.query) + data_request['arguments']['limit'] = req.query.limit; + if ('sort' in req.query) + data_request['arguments']['sort'] = filter.sort_param_to_json(req.query.sort); + if ('search' in req.query) + data_request['arguments']['search'] = filter.search_param_to_json(req.query.search); + + execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); +}) + + /** * @api {get} /rules/files Get files of rules * @apiName GetRulesFiles diff --git a/models/wazuh-api.py b/models/wazuh-api.py index 99b05eaf..25650eef 100644 --- a/models/wazuh-api.py +++ b/models/wazuh-api.py @@ -241,6 +241,7 @@ def usage(): '/rules': Rule.get_rules, '/rules/groups': Rule.get_groups, '/rules/pci': Rule.get_pci, + '/rules/gdpr': Rule.get_gdpr, '/rules/files': Rule.get_rules_files, # Syscheck @@ -277,4 +278,3 @@ def usage(): print_json("Wazuh-Python Internal Error: {0}".format(str(e)), 1000) if debug: raise - diff --git a/test/test_rules.js b/test/test_rules.js index b9fd6abb..19c3a359 100644 --- a/test/test_rules.js +++ b/test/test_rules.js @@ -15,6 +15,7 @@ var request = require('supertest'); var common = require('./common.js'); process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; +rule_fields = ['status', 'gdpr', 'pci', 'description', 'path', 'file', 'level', 'groups', 'id', 'details'] describe('Rules', function() { @@ -34,7 +35,7 @@ describe('Rules', function() { res.body.error.should.equal(0); res.body.data.totalItems.should.be.above(0); res.body.data.items.should.be.instanceof(Array) - res.body.data.items[0].should.have.properties(['status', 'pci', 'description', 'path', 'file', 'level', 'groups', 'id', 'details']); + res.body.data.items[0].should.have.properties(rule_fields); done(); }); }); @@ -52,7 +53,7 @@ describe('Rules', function() { res.body.error.should.equal(0); res.body.data.items.should.be.instanceof(Array).and.have.lengthOf(1); - res.body.data.items[0].should.have.properties(['status', 'pci', 'description', 'path', 'file', 'level', 'groups', 'id', 'details']); + res.body.data.items[0].should.have.properties(rule_fields); res.body.data.items[0].id.should.equal(1); done(); }); @@ -72,7 +73,7 @@ describe('Rules', function() { res.body.error.should.equal(0); res.body.data.totalItems.should.be.above(0); res.body.data.items.should.be.instanceof(Array) - res.body.data.items[0].should.have.properties(['status', 'pci', 'description', 'path', 'file', 'level', 'groups', 'id', 'details']); + res.body.data.items[0].should.have.properties(rule_fields); done(); }); }); @@ -91,7 +92,7 @@ describe('Rules', function() { res.body.error.should.equal(0); res.body.data.totalItems.should.be.above(0); res.body.data.items.should.be.instanceof(Array) - res.body.data.items[0].should.have.properties(['status', 'pci', 'description', 'path', 'file', 'level', 'groups', 'id', 'details']); + res.body.data.items[0].should.have.properties(rule_fields); done(); }); }); @@ -142,7 +143,7 @@ describe('Rules', function() { res.body.error.should.equal(0); res.body.data.totalItems.should.be.above(0); res.body.data.items.should.be.instanceof(Array) - res.body.data.items[0].should.have.properties(['status', 'pci', 'description', 'path', 'file', 'level', 'groups', 'id', 'details']); + res.body.data.items[0].should.have.properties(rule_fields); done(); }); }); @@ -161,7 +162,7 @@ describe('Rules', function() { res.body.error.should.equal(0); res.body.data.totalItems.should.be.above(0); res.body.data.items.should.be.instanceof(Array) - res.body.data.items[0].should.have.properties(['status', 'pci', 'description', 'path', 'file', 'level', 'groups', 'id', 'details']); + res.body.data.items[0].should.have.properties(rule_fields); done(); }); }); @@ -180,7 +181,7 @@ describe('Rules', function() { res.body.error.should.equal(0); res.body.data.totalItems.should.be.above(0); res.body.data.items.should.be.instanceof(Array) - res.body.data.items[0].should.have.properties(['status', 'pci', 'description', 'path', 'file', 'level', 'groups', 'id', 'details']); + res.body.data.items[0].should.have.properties(rule_fields); done(); }); }); @@ -199,7 +200,7 @@ describe('Rules', function() { res.body.error.should.equal(0); res.body.data.totalItems.should.be.above(0); res.body.data.items.should.be.instanceof(Array) - res.body.data.items[0].should.have.properties(['status', 'pci', 'description', 'path', 'file', 'level', 'groups', 'id', 'details']); + res.body.data.items[0].should.have.properties(rule_fields); done(); }); }); @@ -218,7 +219,7 @@ describe('Rules', function() { res.body.error.should.equal(0); res.body.data.totalItems.should.be.above(0); res.body.data.items.should.be.instanceof(Array) - res.body.data.items[0].should.have.properties(['status', 'pci', 'description', 'path', 'file', 'level', 'groups', 'id', 'details']); + res.body.data.items[0].should.have.properties(rule_fields); done(); }); }); @@ -237,7 +238,7 @@ describe('Rules', function() { res.body.error.should.equal(0); res.body.data.totalItems.should.be.above(0); res.body.data.items.should.be.instanceof(Array) - res.body.data.items[0].should.have.properties(['status', 'pci', 'description', 'path', 'file', 'level', 'groups', 'id', 'details']); + res.body.data.items[0].should.have.properties(rule_fields); done(); }); }); @@ -256,11 +257,29 @@ describe('Rules', function() { res.body.error.should.equal(0); res.body.data.totalItems.should.be.above(0); res.body.data.items.should.be.instanceof(Array) - res.body.data.items[0].should.have.properties(['status', 'pci', 'description', 'path', 'file', 'level', 'groups', 'id', 'details']); + res.body.data.items[0].should.have.properties(rule_fields); done(); }); }); + it('Filters: gdpr', function(done) { + request(common.url) + .get("/rules?gdpr=30.1.g") + .auth(common.credentials.user, common.credentials.password) + .expect("Content-type",/json/) + .expect(200) + .end(function(err,res){ + if (err) return done(err); + + res.body.should.have.properties(['error', 'data']); + + res.body.error.should.equal(0); + res.body.data.totalItems.should.be.above(0); + res.body.data.items.should.be.instanceof(Array) + res.body.data.items[0].should.have.properties(rule_fields); + done(); + }); + }); }); // GET/rules describe('GET/rules/groups', function() { @@ -453,6 +472,101 @@ describe('Rules', function() { }); // GET/rules/pci + describe('GET/rules/gdpr', function() { + + it('Request', function(done) { + request(common.url) + .get("/rules/gdpr") + .auth(common.credentials.user, common.credentials.password) + .expect("Content-type",/json/) + .expect(200) + .end(function(err,res){ + if (err) return done(err); + + res.body.should.have.properties(['error', 'data']); + + res.body.error.should.equal(0); + res.body.data.totalItems.should.be.above(0); + res.body.data.items.should.be.instanceof(Array); + res.body.data.items[0].should.be.string; + done(); + }); + }); + + it('Pagination', function(done) { + request(common.url) + .get("/rules/gdpr?offset=0&limit=1") + .auth(common.credentials.user, common.credentials.password) + .expect("Content-type",/json/) + .expect(200) + .end(function(err,res){ + if (err) return done(err); + + res.body.should.have.properties(['error', 'data']); + + res.body.error.should.equal(0); + res.body.data.items.should.be.instanceof(Array).and.have.lengthOf(1); + res.body.data.items[0].should.be.string; + done(); + }); + }); + + it('Sort', function(done) { + request(common.url) + .get("/rules/gdpr?sort=-") + .auth(common.credentials.user, common.credentials.password) + .expect("Content-type",/json/) + .expect(200) + .end(function(err,res){ + if (err) return done(err); + + res.body.should.have.properties(['error', 'data']); + + res.body.error.should.equal(0); + res.body.data.totalItems.should.be.above(0); + res.body.data.items.should.be.instanceof(Array); + res.body.data.items[0].should.be.string; + done(); + }); + }); + + it('Search', function(done) { + request(common.url) + .get("/rules/gdpr?search=30") + .auth(common.credentials.user, common.credentials.password) + .expect("Content-type",/json/) + .expect(200) + .end(function(err,res){ + if (err) return done(err); + + res.body.should.have.properties(['error', 'data']); + + res.body.error.should.equal(0); + res.body.data.totalItems.should.be.above(0); + res.body.data.items.should.be.instanceof(Array); + res.body.data.items[0].should.be.string; + done(); + }); + }); + + it('Filters: Invalid filter', function(done) { + request(common.url) + .get("/rules/gdpr?random") + .auth(common.credentials.user, common.credentials.password) + .expect("Content-type",/json/) + .expect(400) + .end(function(err,res){ + if (err) return done(err); + + res.body.should.have.properties(['error', 'message']); + + res.body.error.should.equal(604); + done(); + }); + }); + + }); // GET/rules/gdpr + describe('GET/rules/files', function() { it('Request', function(done) { @@ -611,7 +725,7 @@ describe('Rules', function() { res.body.error.should.equal(0); res.body.data.totalItems.should.be.above(0); res.body.data.items.should.be.instanceof(Array); - res.body.data.items[0].should.have.properties(['status', 'pci', 'description', 'path', 'file', 'level', 'groups', 'id', 'details']); + res.body.data.items[0].should.have.properties(rule_fields); res.body.data.items[0].id.should.equal(1002); done(); }); @@ -630,7 +744,7 @@ describe('Rules', function() { res.body.error.should.equal(0); res.body.data.items.should.be.instanceof(Array).and.have.lengthOf(1); - res.body.data.items[0].should.have.properties(['status', 'pci', 'description', 'path', 'file', 'level', 'groups', 'id', 'details']); + res.body.data.items[0].should.have.properties(rule_fields); res.body.data.items[0].id.should.equal(1002); done(); }); @@ -650,7 +764,7 @@ describe('Rules', function() { res.body.error.should.equal(0); res.body.data.totalItems.should.be.above(0); res.body.data.items.should.be.instanceof(Array) - res.body.data.items[0].should.have.properties(['status', 'pci', 'description', 'path', 'file', 'level', 'groups', 'id', 'details']); + res.body.data.items[0].should.have.properties(rule_fields); res.body.data.items[0].id.should.equal(1002); done(); }); @@ -670,7 +784,7 @@ describe('Rules', function() { res.body.error.should.equal(0); res.body.data.totalItems.should.be.above(0); res.body.data.items.should.be.instanceof(Array) - res.body.data.items[0].should.have.properties(['status', 'pci', 'description', 'path', 'file', 'level', 'groups', 'id', 'details']); + res.body.data.items[0].should.have.properties(rule_fields); res.body.data.items[0].id.should.equal(1002); done(); });