├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── SECURITY.md ├── app.js ├── configuration ├── auth │ └── user ├── config.js ├── preloaded_vars.conf └── ssl │ └── .gitignore ├── controllers ├── active_response.js ├── agents.js ├── cache.js ├── ciscat.js ├── cluster.js ├── decoders.js ├── experimental.js ├── index.js ├── lists.js ├── manager.js ├── mitre.js ├── rootcheck.js ├── rules.js ├── security_configuration_assessment.js ├── summary.js ├── syscheck.js └── syscollector.js ├── doc ├── README.md └── generate_rst.py ├── examples ├── api-client.ps1 ├── api-client.py ├── api-register-agent.ps1 ├── api-register-agent.py └── api-register-agent.sh ├── helpers ├── check.js ├── errors.js ├── execute.js ├── files.js ├── filters.js ├── input_validation.js ├── logger.js ├── request_templates.js └── response_handler.js ├── install_api.sh ├── models └── wazuh-api.py ├── package.json ├── scripts ├── bump_version.sh ├── configure_api.sh ├── install_daemon.sh ├── wazuh-api └── wazuh-api.service └── test ├── README.md ├── common.js ├── data ├── agent.conf.xml ├── invalid.conf.xml └── wrong.conf.xml ├── environment ├── docker │ ├── centos │ │ ├── .env │ │ ├── docker-compose.yml │ │ ├── wazuh-agent-outdated │ │ │ ├── Dockerfile │ │ │ ├── entrypoint.sh │ │ │ └── wazuh-repository.txt │ │ ├── wazuh-agent │ │ │ ├── Dockerfile │ │ │ ├── agent-ossec.conf │ │ │ ├── entrypoint.sh │ │ │ └── wazuh-repository.txt │ │ └── wazuh-manager │ │ │ ├── Dockerfile │ │ │ ├── entrypoint.sh │ │ │ ├── generate_api_doc.sh │ │ │ ├── master-ossec.conf │ │ │ ├── preloaded-vars.conf │ │ │ ├── run_mocha_tests.sh │ │ │ └── worker-ossec.conf │ └── ubuntu │ │ ├── .env │ │ ├── docker-compose.yml │ │ ├── wazuh-agent-outdated │ │ ├── Dockerfile │ │ └── entrypoint.sh │ │ ├── wazuh-agent │ │ ├── Dockerfile │ │ ├── agent-ossec.conf │ │ └── entrypoint.sh │ │ └── wazuh-manager │ │ ├── Dockerfile │ │ ├── entrypoint.sh │ │ ├── generate_api_doc.sh │ │ ├── master-ossec.conf │ │ ├── preloaded-vars.conf │ │ ├── run_mocha_tests.sh │ │ └── worker-ossec.conf └── vagrant │ ├── Vagrantfile │ ├── configure_manager.sh │ ├── install_agent.sh │ ├── ossec_agents.conf │ └── ossec_master.conf ├── test_active_response.js ├── test_agents.js ├── test_agents_2.js ├── test_app.js ├── test_cluster.js ├── test_decoders.js ├── test_lists.js ├── test_manager.js ├── test_mitre.js ├── test_rootcheck.js ├── test_rules.js ├── test_sca.js ├── test_summary.js ├── test_syscheck.js ├── test_syscollector.js └── utils └── send_to_wdb.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # NodeJS 3 | node_modules 4 | 5 | 6 | # Temp files 7 | scripts/*.tmp 8 | 9 | # Documentation 10 | doc/build/ 11 | 12 | # Python 13 | *.pyc 14 | *.egg-info 15 | *.egg 16 | 17 | # Framework 18 | framework/docs/build/ 19 | framework/dist/ 20 | framework/build/ 21 | framework/lib/ 22 | 23 | # Installation files 24 | configuration/auth/htpasswd 25 | 26 | # Vagrant folder 27 | test/environment/vagrant/.vagrant 28 | test/environment/vagrant/*.log 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wazuh RESTful API 2 | 3 | [![Slack](https://img.shields.io/badge/slack-join-blue.svg)](https://wazuh.com/community/join-us-on-slack/) 4 | [![Email](https://img.shields.io/badge/email-join-blue.svg)](https://groups.google.com/forum/#!forum/wazuh) 5 | [![Documentation](https://img.shields.io/badge/docs-view-green.svg)](https://documentation.wazuh.com) 6 | [![Documentation](https://img.shields.io/badge/web-view-green.svg)](https://wazuh.com) 7 | 8 | Wazuh API is an open source RESTful API to interact with Wazuh from your own application or with a simple web browser or tools like cURL. 9 | 10 | Our goal is to completely manage Wazuh remotely. Perform everyday actions like adding an agent, check configuration, or look for syscheck files are now simplest using Wazuh API. 11 | 12 | Wazuh API capabilities: 13 | 14 | - Agents management 15 | - Manager overview 16 | - Rootcheck control & search 17 | - Security configuration assessment (SCA) 18 | - Syscheck control & search 19 | - Ruleset information 20 | - Statistical Information 21 | - HTTPS and User authentication 22 | - Error Handling 23 | 24 | ## Documentation 25 | 26 | #### How to install 27 | 28 | [Installation guide](https://documentation.wazuh.com/current/installation-guide/installing-wazuh-manager/index.html) 29 | 30 | #### Request reference 31 | 32 | [Full request reference](https://documentation.wazuh.com/current/user-manual/api/reference.html#request-list) 33 | 34 | #### Full documentation 35 | 36 | [Full documentation](https://documentation.wazuh.com/current/user-manual/api/index.html) 37 | 38 | ## Branches 39 | 40 | * `stable` branch on correspond to the last Wazuh API stable version. 41 | * `master` branch contains the latest code, be aware of possible bugs on this branch. 42 | * `development` branch includes all the new features we're adding and testing. 43 | 44 | ## Contribute 45 | 46 | If you want to contribute to our project please don't hesitate to send a pull request. You can also join our users [mailing list](https://groups.google.com/d/forum/wazuh), by sending an email to [wazuh+subscribe@googlegroups.com](mailto:wazuh+subscribe@googlegroups.com), to ask questions and participate in discussions. 47 | 48 | 49 | ## References 50 | 51 | * [Wazuh website](http://wazuh.com) 52 | * [Wazuh repositories](http://github.com/wazuh) 53 | * [Wazuh documentation](http://documentation.wazuh.com) 54 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Wazuh Open Source Project Security Policy 2 | 3 | Version: 2023-06-12 4 | 5 | ## Introduction 6 | This document outlines the Security Policy for Wazuh's open source projects. It emphasizes our commitment to maintain a secure environment for our users and contributors, and reflects our belief in the power of collaboration to identify and resolve security vulnerabilities. 7 | 8 | ## Scope 9 | This policy applies to all open source projects developed, maintained, or hosted by Wazuh. 10 | 11 | ## Reporting Security Vulnerabilities 12 | If you believe you've discovered a potential security vulnerability in one of our open source projects, we strongly encourage you to report it to us responsibly. 13 | 14 | Please submit your findings as security advisories under the "Security" tab in the relevant GitHub repository. Alternatively, you may send the details of your findings to [security@wazuh.com](mailto:security@wazuh.com). 15 | 16 | ## Vulnerability Disclosure Policy 17 | Upon receiving a report of a potential vulnerability, our team will initiate an investigation. If the reported issue is confirmed as a vulnerability, we will take the following steps: 18 | 19 | 1. Acknowledgment: We will acknowledge the receipt of your vulnerability report and begin our investigation. 20 | 2. Validation: We will validate the issue and work on reproducing it in our environment. 21 | 3. Remediation: We will work on a fix and thoroughly test it 22 | 4. Release & Disclosure: After 90 days from the discovery of the vulnerability, or as soon as a fix is ready and thoroughly tested (whichever comes first), we will release a security update for the affected project. We will also publicly disclose the vulnerability by publishing a CVE (Common Vulnerabilities and Exposures) and acknowledging the discovering party. 23 | 5. Exceptions: In order to preserve the security of the Wazuh community at large, we might extend the disclosure period to allow users to patch their deployments. 24 | 25 | This 90-day period allows for end-users to update their systems and minimizes the risk of widespread exploitation of the vulnerability. 26 | 27 | ## Automatic Scanning 28 | We leverage GitHub Actions to perform automated scans of our supply chain. These scans assist us in identifying vulnerabilities and outdated dependencies in a proactive and timely manner. 29 | 30 | ## Credit 31 | We believe in giving credit where credit is due. If you report a security vulnerability to us, and we determine that it is a valid vulnerability, we will publicly credit you for the discovery when we disclose the vulnerability. If you wish to remain anonymous, please indicate so in your initial report. 32 | 33 | We do appreciate and encourage feedback from our community, but currently we do not have a bounty program. We might start bounty programs in the future. 34 | 35 | ## Compliance with this Policy 36 | We consider the discovery and reporting of security vulnerabilities an important public service. We encourage responsible reporting of any vulnerabilities that may be found in our site or applications. 37 | 38 | Furthermore, we will not take legal action against or suspend or terminate access to the site or services of those who discover and report security vulnerabilities in accordance with this policy because of the fact. 39 | 40 | We ask that all users and contributors respect this policy and the security of our community's users by disclosing vulnerabilities to us in accordance with this policy. 41 | 42 | ## Changes to this Security Policy 43 | This policy may be revised from time to time. Each version of the policy will be identified at the top of the page by its effective date. 44 | 45 | If you have any questions about this Security Policy, please contact us at [security@wazuh.com](mailto:security@wazuh.com) -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh API RESTful 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | if (process.getuid() !== 0){ 13 | console.log('A root user is required to start the API.'); 14 | process.exit(1); 15 | } 16 | 17 | /********************************************/ 18 | /* Root actions 19 | /********************************************/ 20 | try { 21 | var auth = require("http-auth"); 22 | } catch (e) { 23 | console.log("Dependencies not found. Try 'npm install' in /var/ossec/api. Exiting..."); 24 | process.exit(1); 25 | } 26 | 27 | const check = require('./helpers/check'); 28 | 29 | // Get configuration 30 | config = require('./configuration/config'); 31 | if (check.configuration_file() < 0) { 32 | setTimeout(function(){ process.exit(1); }, 500); 33 | return; 34 | } 35 | 36 | // Get credentials 37 | if (config.basic_auth.toLowerCase() == "yes"){ 38 | var auth_secure = auth.basic({ 39 | realm: "OSSEC API", 40 | file: __dirname + "/configuration/auth/user" 41 | }); 42 | } 43 | 44 | // Get Certs 45 | var options = {}; 46 | 47 | var fs = require('fs'); 48 | if (config.https.toLowerCase() == "yes"){ 49 | var api_route = config.ossec_path + '/api/'; 50 | var option_paths = {}; 51 | 52 | // Cert and key 53 | option_paths.key = config.https_key || 'configuration/ssl/server.key'; 54 | if (option_paths.key.charAt(0) != '/'){ 55 | option_paths.key = api_route + option_paths.key; 56 | } 57 | 58 | option_paths.cert = config.https_cert || 'configuration/ssl/server.crt'; 59 | if (option_paths.cert.charAt(0) != '/'){ 60 | option_paths.cert = api_route + option_paths.cert; 61 | } 62 | 63 | options.key = fs.readFileSync(option_paths.key); 64 | options.cert = fs.readFileSync(option_paths.cert); 65 | 66 | // CA 67 | var use_ca = config.https_use_ca || 'no'; 68 | if (use_ca.toLowerCase() == "yes"){ 69 | option_paths.ca = config.https_ca || 'configuration/ssl/ca.crt'; 70 | if (option_paths.ca.charAt(0) != '/'){ 71 | option_paths.ca = api_route + option_paths.ca; 72 | } 73 | 74 | options.ca = fs.readFileSync(option_paths.ca); 75 | } 76 | if (config.secureProtocol && config.secureProtocol != "") options.secureProtocol = config.secureProtocol; 77 | if (config.ciphers && config.ciphers != "") options.ciphers = config.ciphers; 78 | if (config.honorCipherOrder) options.honorCipherOrder = config.honorCipherOrder; 79 | if (config.secureOptions) options.secureOptions = config.secureOptions; 80 | } 81 | 82 | 83 | /********************************************/ 84 | /* Drop privileges 85 | /********************************************/ 86 | if (config.drop_privileges || config.drop_privileges == undefined) { 87 | try { 88 | process.setgid('ossec'); 89 | process.setuid('ossec'); 90 | } catch(err) { 91 | console.log('Drop privileges failed: ' + err.message); 92 | process.exit(1); 93 | } 94 | } 95 | 96 | /********************************************/ 97 | /* Modules, vars and global vars 98 | /********************************************/ 99 | try { 100 | var express = require('express'); 101 | var bodyParser = require('body-parser'); 102 | var cors = require('cors') 103 | var moment = require('moment'); 104 | res_h = require('./helpers/response_handler'); 105 | logger = require('./helpers/logger'); 106 | } catch (e) { 107 | console.log("Dependencies not found. Try 'npm install' in /var/ossec/api. Exiting..."); 108 | process.exit(1); 109 | } 110 | 111 | api_path = __dirname; 112 | python_bin = config.ossec_path + '/framework/python/bin/python3'; 113 | 114 | /********************************************/ 115 | /* Config APP 116 | /********************************************/ 117 | info_package = require('./package.json'); 118 | var version_mmp = info_package.version.split('.'); // major.minor.patch 119 | var current_mm_version = version_mmp[0] + '.' + version_mmp[1]; // major.minor 120 | 121 | if (process.argv.length == 3 && process.argv[2] == "-f") 122 | logger.set_foreground(); 123 | 124 | if (check.wazuh(logger) < 0) { 125 | setTimeout(function(){ process.exit(1); }, 500); 126 | return; 127 | } 128 | 129 | var port = process.env.PORT || config.port; 130 | 131 | if (config.host != "0.0.0.0") 132 | var host = config.host; 133 | 134 | var app = express(); 135 | 136 | // CORS 137 | if (config.cors.toLowerCase() == "yes"){ 138 | app.use(cors()); 139 | } 140 | 141 | // Basic authentication 142 | if (config.basic_auth.toLowerCase() == "yes"){ 143 | app.use(auth.connect(auth_secure)); 144 | 145 | auth_secure.on('fail', (result, req) => { 146 | logger.set_user(result.user); 147 | var log_msg = "[" + req.connection.remoteAddress + "] " + "User: \"" + result.user + "\" - Authentication failed."; 148 | logger.log(log_msg); 149 | }); 150 | 151 | auth_secure.on('error', (error, req) => { 152 | logger.set_user(""); 153 | var log_msg = "[" + req.connection.remoteAddress + "] Authentication error: " + error.code + " - " + error.message; 154 | logger.log(log_msg); 155 | }); 156 | } 157 | 158 | // temporary 159 | if (config.ld_library_path) { 160 | logger.warning("LD library path configuration is deprecated."); 161 | } 162 | if (config.python) { 163 | logger.warning("Python configuration is deprecated. Using " + python_bin); 164 | } 165 | 166 | 167 | // Body 168 | app.use(bodyParser.json()); 169 | app.use(bodyParser.urlencoded({extended: true})); 170 | app.use(bodyParser.text({type:"application/xml", limit:"10mb"})); 171 | app.use(bodyParser.raw({type:"application/octet-stream", limit:"10mb"})); 172 | 173 | 174 | /** 175 | * Check Wazuh app version 176 | * Using: Header: "wazuh-app-version: X.Y.Z" 177 | */ 178 | app.use(function(req, res, next) { 179 | var go_next = true; 180 | var app_version_header = req.get('wazuh-app-version'); 181 | var regex_version = /^\d+\.\d+\.\d+$/i; 182 | 183 | if (typeof app_version_header != 'undefined'){ 184 | if (!regex_version.test(app_version_header)){ 185 | go_next = false; 186 | res_h.bad_request(req, res, 801); 187 | } 188 | else{ 189 | var app_version_mmp = app_version_header.split('.'); // major.minor.patch 190 | var app_mm_version = app_version_mmp[0] + '.' + app_version_mmp[1]; // major.minor 191 | 192 | if (app_mm_version != current_mm_version){ 193 | go_next = false; 194 | res_h.bad_request(req, res, 802, "Expected version '" + current_mm_version + ".x', and found '" + app_mm_version + ".x'."); 195 | } 196 | } 197 | } 198 | 199 | if (go_next) 200 | next(); 201 | }); 202 | 203 | // Controllers 204 | app.use("/", require('./controllers')); 205 | 206 | // APP Errors 207 | app.use (function (err, req, res, next){ 208 | 209 | if ( err == "Error: invalid json" ){ 210 | logger.debug(req.connection.remoteAddress + " " + req.method + " " + req.path); 211 | res_h.bad_request(req, res, 607); 212 | } 213 | else if ('status' in err && err.status == 400){ 214 | var msg = ""; 215 | if ('body' in err) 216 | msg = "Body is not correct."; 217 | res_h.bad_request(req, res, 614, msg); 218 | } 219 | else if (err == "PayloadTooLargeError: request entity too large"){ 220 | res_h.bad_request(req, res, 701); 221 | } 222 | else{ 223 | logger.log("Internal Error"); 224 | if(err.stack) 225 | logger.log(err.stack); 226 | logger.log("Exiting..."); 227 | setTimeout(function(){ process.exit(1); }, 500); 228 | } 229 | }); 230 | 231 | /********************************************/ 232 | /* Create server 233 | /********************************************/ 234 | if (config.https.toLowerCase() == "yes"){ 235 | var https = require('https'); 236 | var server = https.createServer(options, app).listen(port, host, function(){ 237 | logger.log("Listening on: https://" + server.address().address + ":" + port); 238 | }); 239 | } 240 | else{ 241 | var http = require('http'); 242 | var server = http.createServer(app).listen(port, host, function(){ 243 | logger.log("Listening on: http://" + server.address().address + ":" + port); 244 | }); 245 | } 246 | 247 | /********************************************/ 248 | /* Event handler 249 | /********************************************/ 250 | process.on('uncaughtException', function(err) { 251 | 252 | if (err.errno == "EADDRINUSE") 253 | logger.log("Error: Address in use (port " + port + "): Close the program using that port or change the port.") 254 | else { 255 | logger.log("Internal Error: uncaughtException"); 256 | if(err.stack) 257 | logger.log(err.stack); 258 | } 259 | 260 | logger.log("Exiting..."); 261 | setTimeout(function(){ process.exit(1); }, 500); 262 | }); 263 | 264 | process.on('SIGTERM', function() { 265 | logger.log("Exiting... (SIGTERM)"); 266 | setTimeout(function(){ process.exit(1); }, 500); 267 | }); 268 | 269 | process.on('SIGINT', function() { 270 | logger.log("Exiting... (SIGINT)"); 271 | setTimeout(function(){ process.exit(1); }, 500); 272 | }); 273 | -------------------------------------------------------------------------------- /configuration/auth/user: -------------------------------------------------------------------------------- 1 | foo:$apr1$qdSLixxe$4syV8RRHWSSsvo8Gu6rZ.1 2 | -------------------------------------------------------------------------------- /configuration/config.js: -------------------------------------------------------------------------------- 1 | 2 | var config = {}; 3 | 4 | // Basic configuration 5 | 6 | // Path 7 | config.ossec_path = "/var/ossec"; 8 | // The host to bind the API to. 9 | config.host = "0.0.0.0"; 10 | // TCP Port used by the API. 11 | config.port = "55000"; 12 | // Use HTTP protocol over TLS/SSL. Values: yes, no. 13 | config.https = "no"; 14 | // Use HTTP authentication. Values: yes, no. 15 | config.basic_auth = "yes"; 16 | //In case the API run behind a proxy server, turn to "yes" this feature. Values: yes, no. 17 | config.BehindProxyServer = "no"; 18 | 19 | // HTTPS Certificates 20 | //config.https_key = "configuration/ssl/server.key" 21 | //config.https_cert = "configuration/ssl/server.crt" 22 | //config.https_use_ca = "no" 23 | //config.https_ca = "configuration/ssl/ca.crt" 24 | 25 | // Advanced configuration 26 | 27 | // Values for API log: disabled, info, warning, error, debug (each level includes the previous level). 28 | config.logs = "info"; 29 | // Cross-origin resource sharing. Values: yes, no. 30 | config.cors = "yes"; 31 | // Cache (time in milliseconds) 32 | config.cache_enabled = "yes"; 33 | config.cache_debug = "no"; 34 | config.cache_time = "750"; 35 | // Log path 36 | config.log_path = config.ossec_path + "/logs/api.log"; 37 | 38 | // Option to force the use of authd to remove and add agents 39 | config.use_only_authd = false; 40 | 41 | // Option to drop privileges (run as ossec) 42 | config.drop_privileges = true; 43 | 44 | // Activate features still under development 45 | config.experimental_features = false; 46 | 47 | /************************* SSL OPTIONS ****************************************/ 48 | // SSL protocol 49 | 50 | // SSL protocol to use. All available secure protocols available at: 51 | // https://www.openssl.org/docs/man1.0.2/ssl/ssl.html#DEALING-WITH-PROTOCOL-METHODS 52 | config.secureProtocol = "TLSv1_2_method"; 53 | try { 54 | // Disable the use of SSLv3, TLSv1.1 and TLSv1.0. All available secureOptions at: 55 | // https://nodejs.org/api/crypto.html#crypto_openssl_options 56 | const crypto = require('crypto'); 57 | config.secureOptions = crypto.constants.SSL_OP_NO_SSLv3 | 58 | crypto.constants.SSL_OP_NO_TLSv1 | 59 | crypto.constants.SSL_OP_NO_TLSv1_1; 60 | } catch (err) { 61 | console.log("Could not configure NodeJS to avoid unsecure SSL/TLS protocols: " + err) 62 | } 63 | 64 | // SSL ciphersuit 65 | 66 | // When choosing a cipher, use the server's preferences instead of the client 67 | // preferences. When not set, the SSL server will always follow the clients 68 | // preferences. More info at: 69 | // https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_options.html 70 | config.honorCipherOrder = true; 71 | // Modify default ciphersuit. More info: 72 | // https://nodejs.org/api/tls.html#tls_modifying_the_default_tls_cipher_suite 73 | config.ciphers = ""; 74 | 75 | module.exports = config; 76 | -------------------------------------------------------------------------------- /configuration/preloaded_vars.conf: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2017 Wazuh, Inc.All rights reserved. 2 | # Wazuh.com 3 | # This program is a free software; you can redistribute it 4 | # and/or modify it under the terms of the GNU General Public 5 | # License (version 2) as published by the FSF - Free Software 6 | # Foundation. 7 | # 8 | # preloaded-vars.conf for Wazuh API 9 | # 10 | # Use this file to customize your installations. 11 | # It will make the install.sh script pre-load some 12 | # specific options to make it run automatically 13 | # or with less questions. 14 | 15 | #------------------- Variables for install_api.sh -------------------# 16 | # Reinstall Wazuh 17 | # REINSTALL=n 18 | 19 | # Remove actual install 20 | # REMOVE=n 21 | 22 | # Installation directory 23 | # DIRECTORY="/var/ossec" 24 | 25 | # Disable HTTPS 26 | # DISABLE_HTTPS=n 27 | 28 | #------------------ Variables for configure_api.sh ------------------# 29 | # Port 30 | # PORT=55000 31 | 32 | # Enable HTTPS 33 | # HTTPS=Y 34 | 35 | # Enable user authentication 36 | # AUTH=Y 37 | 38 | # Change proxy 39 | # PROXY=N 40 | 41 | #--------------- Variables for certificate generation ---------------# 42 | # COUNTRY="US" 43 | # STATE="State" 44 | # LOCALITY="Locality" 45 | # ORG_NAME="Org Name" 46 | # ORG_UNIT="Org Unit Name" 47 | # COMMON_NAME="Common Name" 48 | # PASSWORD="password" 49 | 50 | #--------------------- Variables for basic auth ---------------------# 51 | # USER=foo 52 | # PASS=bar 53 | 54 | #### exit ? ### 55 | -------------------------------------------------------------------------------- /configuration/ssl/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /controllers/active_response.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | 13 | var router = require('express').Router(); 14 | 15 | 16 | /** 17 | * @api {put} /active-response/:agent_id Run an AR command in the agent 18 | * @apiName PutARAgentIdCommand 19 | * @apiGroup Command 20 | * 21 | * @apiParam {Number} agent_id Agent ID. 22 | * @apiParam {String} command Command running in the agent. If this value starts by !, then it refers to a script name instead of a command name. 23 | * @apiParam {Boolean} custom Whether the specified command is a custom command or not. 24 | * @apiParam {String[]} arguments Array with command arguments. 25 | * 26 | * @apiDescription Runs an Active Response command on a specified agent. 27 | * 28 | * @apiExample {curl} Example usage*: 29 | * curl -u foo:bar -k -X PUT -d '{"command":"restart-ossec0", "arguments": ["-", "null", "(from_the_server)", "(no_rule_id)"]}' -H 'Content-Type:application/json' "https://127.0.0.1:55000/active-response/001?pretty" 30 | */ 31 | router.put('/:agent_id', function(req, res) { 32 | logger.debug(req.connection.remoteAddress + " PUT /active-response/:agent_id"); 33 | 34 | var data_request = {'function': '/PUT/active-response/:agent_id', 'arguments': {}}; 35 | 36 | if (!filter.check(req.params, {'agent_id':'numbers'}, req, res)) // Filter with error 37 | return; 38 | 39 | var filters = {'command':'active_response_command', 'custom':'boolean', 'arguments': 'array'}; 40 | if (!filter.check(req.body, filters, req, res)) // Filter with error 41 | return; 42 | 43 | data_request['arguments']['agent_id'] = req.params.agent_id; 44 | data_request['arguments']['command'] = req.body.command; 45 | data_request['arguments']['custom'] = req.body.custom; 46 | data_request['arguments']['arguments'] = req.body.arguments; 47 | 48 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 49 | }) 50 | 51 | module.exports = router; 52 | -------------------------------------------------------------------------------- /controllers/cache.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | 13 | var router = require('express').Router(); 14 | 15 | /** 16 | * @api {get} /cache Get cache index 17 | * @apiName GetCache 18 | * @apiGroup Info 19 | * 20 | * 21 | * @apiDescription Returns current cache index. 22 | * 23 | * @apiExample {curl} Example usage: 24 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/cache?pretty" 25 | * 26 | */ 27 | router.get('/', function(req, res, next) { 28 | logger.debug(req.connection.remoteAddress + " GET /cache"); 29 | res_h.send(req, res, { 'error': 0, 'data': apicache.getIndex() }); 30 | }); 31 | 32 | /** 33 | * @api {get} /cache/config Return cache configuration 34 | * @apiName GetCacheConfiguration 35 | * @apiGroup Info 36 | * 37 | * 38 | * @apiDescription Returns cache configuration. 39 | * 40 | * @apiExample {curl} Example usage: 41 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/cache/config?pretty" 42 | * 43 | */ 44 | router.get('/config', function(req, res, next) { 45 | logger.debug(req.connection.remoteAddress + " GET /cache/config"); 46 | res_h.send(req, res, { 'error': 0, 'data': apicache.options() }); 47 | }); 48 | 49 | /** 50 | * @api {delete} /cache Delete cache index 51 | * @apiName DeleteCache 52 | * @apiGroup Delete 53 | * 54 | * 55 | * @apiDescription Clears entire cache. 56 | * 57 | * @apiExample {curl} Example usage: 58 | * curl -u foo:bar -k -X DELETE "https://127.0.0.1:55000/cache?pretty" 59 | * 60 | */ 61 | router.delete('/', function(req, res, next) { 62 | logger.debug(req.connection.remoteAddress + " DELETE /cache"); 63 | res_h.send(req, res, { 'error': 0, 'data': apicache.clear() }); 64 | }); 65 | 66 | /** 67 | * @api {delete} /cache/:group Clear group cache 68 | * @apiName DeleteCacheGroup 69 | * @apiGroup Delete 70 | * 71 | * @apiParam {String} group cache group. 72 | * 73 | * @apiDescription Clears cache of the specified group. 74 | * 75 | * @apiExample {curl} Example usage: 76 | * curl -u foo:bar -k -X DELETE "https://127.0.0.1:55000/cache/mygroup?pretty" 77 | * 78 | */ 79 | router.delete('/:group', function(req, res, next) { 80 | logger.debug(req.connection.remoteAddress + " DELETE /cache/:group"); 81 | 82 | if (!filter.check(req.params, {'group':'alphanumeric_param'}, req, res)) // Filter with error 83 | return; 84 | 85 | res_h.send(req, res, { 'error': 0, 'data': apicache.clear(req.params.group) }); 86 | }); 87 | 88 | 89 | module.exports = router; 90 | -------------------------------------------------------------------------------- /controllers/ciscat.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | 13 | var router = require('express').Router(); 14 | 15 | 16 | /** 17 | * @api {get} /ciscat/:agent_id/results Get CIS-CAT results from an agent 18 | * @apiName GetCiscat_agent 19 | * @apiGroup Results 20 | * 21 | * @apiParam {Number} agent_id Agent ID. 22 | * @apiParam {Number} [offset] First element to return in the collection. 23 | * @apiParam {Number} [limit=500] Maximum number of elements to return. 24 | * @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. 25 | * @apiParam {String} [search] Looks for elements with the specified string. 26 | * @apiParam {String} [select] List of selected fields separated by commas. 27 | * @apiParam {String} [benchmark] Filters by benchmark. 28 | * @apiParam {String} [profile] Filters by evaluated profile. 29 | * @apiParam {Number} [pass] Filters by passed checks. 30 | * @apiParam {Number} [fail] Filters by failed checks. 31 | * @apiParam {Number} [error] Filters by encountered errors. 32 | * @apiParam {Number} [notchecked] Filters by not checked. 33 | * @apiParam {Number} [unknown] Filters by unknown results. 34 | * @apiParam {Number} [score] Filters by final score. 35 | * @apiParam {String} [q] Advanced query filtering. 36 | * 37 | * @apiDescription Returns the agent's ciscat results info 38 | * 39 | * @apiExample {curl} Example usage*: 40 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/ciscat/000/results?pretty&sort=-score" 41 | * 42 | */ 43 | router.get('/:agent_id/results', function (req, res) { 44 | logger.debug(req.connection.remoteAddress + " GET /ciscat/:agent_id/results"); 45 | var filters = {'benchmark': 'alphanumeric_param', 'profile': 'alphanumeric_param', 46 | 'pass': 'alphanumeric_param', 'fail': 'alphanumeric_param', 47 | 'error': 'numbers', 'notchecked': 'numbers', 48 | 'unknown': 'numbers', 'score': 'numbers' 49 | }; 50 | templates.array_request("/ciscat/:agent_id/results", req, res, "ciscat", {'agent_id': 'numbers'}, filters); 51 | }); 52 | 53 | module.exports = router; 54 | -------------------------------------------------------------------------------- /controllers/decoders.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | 13 | var router = require('express').Router(); 14 | 15 | /** 16 | * @api {get} /decoders Get all decoders 17 | * @apiName GetDecoders 18 | * @apiGroup Info 19 | * 20 | * @apiParam {Number} [offset] First element to return in the collection. 21 | * @apiParam {Number} [limit=500] Maximum number of elements to return. 22 | * @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. 23 | * @apiParam {String} [search] Looks for elements with the specified string. 24 | * @apiParam {String} [file] Filters by filename. 25 | * @apiParam {String} [path] Filters by path. 26 | * @apiParam {String="enabled","disabled", "all"} [status] Filters the decoders by status. 27 | * @apiParam {String} [q] Query to filter results by. For example q=name=wazuh 28 | * 29 | * @apiDescription Returns all decoders included in ossec.conf. 30 | * 31 | * @apiExample {curl} Example usage: 32 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/decoders?pretty&offset=0&limit=2&sort=+file,position" 33 | * 34 | */ 35 | router.get('/', cache(), function(req, res) { 36 | param_checks = {} 37 | query_checks = {'status':'alphanumeric_param', 'path':'paths', 'file':'alphanumeric_param'}; 38 | 39 | templates.array_request('/decoders', req, res, "decoders", param_checks, query_checks); 40 | }) 41 | 42 | /** 43 | * @api {get} /decoders/files Get all decoders files 44 | * @apiName GetDecodersFiles 45 | * @apiGroup Info 46 | * 47 | * @apiParam {Number} [offset] First element to return in the collection. 48 | * @apiParam {Number} [limit=500] Maximum number of elements to return. 49 | * @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. 50 | * @apiParam {String} [search] Looks for elements with the specified string. 51 | * @apiParam {String="enabled","disabled", "all"} [status] Filters the decoders by status. 52 | * @apiParam {String} [file] Filters by filename. 53 | * @apiParam {String} [path] Filters by path. 54 | * @apiParam {String} [download] Name of the decoder file to download. 55 | * 56 | * @apiDescription Returns all decoders files included in ossec.conf. 57 | * 58 | * @apiExample {curl} Example usage: 59 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/decoders/files?pretty&offset=0&limit=10&sort=-path" 60 | * 61 | */ 62 | router.get('/files', cache(), function(req, res) { 63 | logger.debug(req.connection.remoteAddress + " GET /decoders/files"); 64 | 65 | req.apicacheGroup = "decoders"; 66 | 67 | var data_request = {'function': '/decoders/files', 'arguments': {}}; 68 | var filters = {'offset': 'numbers', 'limit': 'numbers', 'sort':'sort_param', 'search':'search_param', 'status':'alphanumeric_param', 'download':'alphanumeric_param', 'path':'paths', 'file':'alphanumeric_param'}; 69 | 70 | if (!filter.check(req.query, filters, req, res)) // Filter with error 71 | return; 72 | 73 | if ('offset' in req.query) 74 | data_request['arguments']['offset'] = Number(req.query.offset); 75 | if ('limit' in req.query) 76 | data_request['arguments']['limit'] = Number(req.query.limit); 77 | if ('sort' in req.query) 78 | data_request['arguments']['sort'] = filter.sort_param_to_json(req.query.sort); 79 | if ('search' in req.query) 80 | data_request['arguments']['search'] = filter.search_param_to_json(req.query.search); 81 | if ('status' in req.query) 82 | data_request['arguments']['status'] = req.query.status; 83 | if ('file' in req.query) 84 | data_request['arguments']['file'] = req.query.file; 85 | if ('path' in req.query) 86 | data_request['arguments']['path'] = req.query.path; 87 | 88 | if ('download' in req.query) 89 | res_h.send_file(req, res, req.query.download, 'decoders'); 90 | else 91 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 92 | }) 93 | 94 | /** 95 | * @api {get} /decoders/parents Get all parent decoders 96 | * @apiName GetDecodersParents 97 | * @apiGroup Info 98 | * 99 | * @apiParam {Number} [offset] First element to return in the collection. 100 | * @apiParam {Number} [limit=500] Maximum number of elements to return. 101 | * @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. 102 | * @apiParam {String} [search] Looks for elements with the specified string. 103 | * 104 | * @apiDescription Returns all parent decoders included in ossec.conf. 105 | * 106 | * @apiExample {curl} Example usage: 107 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/decoders/parents?pretty&offset=0&limit=2&sort=-file" 108 | * 109 | */ 110 | router.get('/parents', cache(), function(req, res) { 111 | logger.debug(req.connection.remoteAddress + " GET /decoders/parents"); 112 | 113 | req.apicacheGroup = "decoders"; 114 | 115 | var data_request = {'function': '/decoders', 'arguments': {}}; 116 | var filters = {'offset': 'numbers', 'limit': 'numbers', 'sort': 'sort_param', 'search': 'search_param'}; 117 | 118 | if (!filter.check(req.query, filters, req, res)) // Filter with error 119 | return; 120 | 121 | if ('offset' in req.query) 122 | data_request['arguments']['offset'] = Number(req.query.offset); 123 | if ('limit' in req.query) 124 | data_request['arguments']['limit'] = Number(req.query.limit); 125 | if ('sort' in req.query) 126 | data_request['arguments']['sort'] = filter.sort_param_to_json(req.query.sort); 127 | if ('search' in req.query) 128 | data_request['arguments']['search'] = filter.search_param_to_json(req.query.search); 129 | 130 | data_request['arguments']['filters'] = {'parents': 'True'}; 131 | 132 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 133 | }) 134 | 135 | /** 136 | * @api {get} /decoders/:decoder_name Get decoders by name 137 | * @apiName GetDecodersName 138 | * @apiGroup Info 139 | * 140 | * @apiParam {String} decoder_name Decoder name. 141 | * @apiParam {Number} [offset] First element to return in the collection. 142 | * @apiParam {Number} [limit=500] Maximum number of elements to return. 143 | * @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. 144 | * @apiParam {String} [search] Looks for elements with the specified string. 145 | * 146 | * @apiDescription Returns the decoders with the specified name. 147 | * 148 | * @apiExample {curl} Example usage: 149 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/decoders/apache-errorlog?pretty" 150 | * 151 | */ 152 | router.get('/:decoder_name', cache(), function(req, res) { 153 | logger.debug(req.connection.remoteAddress + " GET /decoders/:decoder_name"); 154 | 155 | req.apicacheGroup = "decoders"; 156 | 157 | var data_request = {'function': '/decoders', 'arguments': {}}; 158 | var filters = {'offset': 'numbers', 'limit': 'numbers', 'sort':'sort_param', 'search':'search_param'}; 159 | 160 | if (!filter.check(req.query, filters, req, res)) // Filter with error 161 | return; 162 | 163 | if ('offset' in req.query) 164 | data_request['arguments']['offset'] = Number(req.query.offset); 165 | if ('limit' in req.query) 166 | data_request['arguments']['limit'] = Number(req.query.limit); 167 | if ('sort' in req.query) 168 | data_request['arguments']['sort'] = filter.sort_param_to_json(req.query.sort); 169 | if ('search' in req.query) 170 | data_request['arguments']['search'] = filter.search_param_to_json(req.query.search); 171 | 172 | if (!filter.check(req.params, {'decoder_name':'names'}, req, res)) // Filter with error 173 | return; 174 | 175 | data_request['arguments']['filters'] = {'name': req.params.decoder_name}; 176 | 177 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 178 | }) 179 | 180 | 181 | 182 | module.exports = router; 183 | -------------------------------------------------------------------------------- /controllers/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | 13 | errors = require('../helpers/errors'); 14 | filter = require('../helpers/filters'); 15 | execute = require('../helpers/execute'); 16 | templates = require('../helpers/request_templates'); 17 | apicache = require('apicache'); 18 | cache = apicache.middleware; 19 | wazuh_control = api_path + "/models/wazuh-api.py"; 20 | 21 | var router = require('express').Router(); 22 | var os = require("os"); 23 | 24 | // Cache options 25 | if (config.cache_enabled.toLowerCase() == "yes"){ 26 | if (config.cache_debug.toLowerCase() == "yes") 27 | cache_debug = true; 28 | else 29 | cache_debug = false; 30 | cache_opt = { debug: cache_debug, defaultDuration: parseInt(config.cache_time)}; 31 | } 32 | else 33 | cache_opt = { enabled: false }; 34 | 35 | apicache.options(cache_opt); 36 | 37 | // Content-type 38 | router.post("*", function(req, res, next) { 39 | var content_type = req.get('Content-Type'); 40 | 41 | if (!content_type || !(content_type == 'application/json' || content_type == 'application/x-www-form-urlencoded' 42 | || content_type == 'application/xml' || content_type == 'application/octet-stream')){ 43 | logger.debug(req.connection.remoteAddress + " POST " + req.path); 44 | res_h.bad_request(req, res, "607"); 45 | } 46 | else 47 | next(); 48 | }); 49 | 50 | // All requests 51 | router.all("*", function(req, res, next) { 52 | var go_next = true; 53 | logger.set_user(req.user); 54 | 55 | if (req.query){ 56 | // Pretty 57 | if ("pretty" in req.query){ 58 | req['pretty'] = true; 59 | delete req.query["pretty"]; 60 | } else { 61 | req['pretty'] = false; 62 | } 63 | // wait for 64 | if ("wait_for_complete" in req.query) { 65 | // Disable timeout in the current API call 66 | execute.set_disable_timeout(true); 67 | delete req.query["wait_for_complete"]; 68 | } 69 | } 70 | 71 | if (go_next) 72 | next(); 73 | }); 74 | 75 | // Controllers 76 | router.use('/agents', require('./agents')); 77 | router.use('/manager', require('./manager')); 78 | router.use('/syscheck', require('./syscheck')); 79 | router.use('/rootcheck', require('./rootcheck')); 80 | router.use('/sca', require('./security_configuration_assessment')); 81 | router.use('/rules', require('./rules')); 82 | router.use('/decoders', require('./decoders')); 83 | router.use('/cache', require('./cache')); 84 | router.use('/cluster', require('./cluster')); 85 | router.use('/syscollector', require('./syscollector')); 86 | router.use('/ciscat', require('./ciscat')); 87 | router.use('/active-response', require('./active_response')); 88 | router.use('/lists', require('./lists')); 89 | router.use('/summary', require('./summary')); 90 | router.use('/mitre', require('./mitre')); 91 | 92 | if (config.experimental_features){ 93 | router.use('/experimental', require('./experimental')); 94 | } 95 | 96 | // Index 97 | router.get('/',function(req, res) { 98 | logger.debug(req.connection.remoteAddress + " GET /"); 99 | data = { 'msg': "Welcome to Wazuh HIDS API", 'api_version': "v" + info_package.version, 'hostname': os.hostname(), 'timestamp': new Date().toString()} 100 | json_res = {'error': 0, 'data': data}; 101 | res_h.send(req, res, json_res); 102 | }); 103 | 104 | // Version 105 | router.get('/version',function(req, res) { 106 | logger.debug(req.connection.remoteAddress + " GET /version"); 107 | 108 | json_res = {'error': 0, 'data': "v" + info_package.version}; 109 | 110 | res_h.send(req, res, json_res); 111 | }); 112 | 113 | // ALWAYS Keep this as the last route 114 | router.all('*',function(req, res) { 115 | logger.debug(req.connection.remoteAddress + " " + req.method + " " + req.path); 116 | json_res = { 'error': 603, 'message': errors.description(603)}; 117 | res_h.send(req, res, json_res, 404); 118 | }); 119 | 120 | 121 | // Router Errors 122 | router.use(function(err, req, res, next){ 123 | logger.log("Internal Error"); 124 | if(err.stack) 125 | logger.log(err.stack); 126 | logger.log("Exiting..."); 127 | 128 | res_h.send(req, res, { error: 3 }, 500) 129 | }); 130 | 131 | 132 | module.exports = router 133 | -------------------------------------------------------------------------------- /controllers/lists.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | 13 | var router = require('express').Router(); 14 | 15 | /** 16 | * @api {get} /lists Get all lists 17 | * @apiName GetLists 18 | * @apiGroup Info 19 | * 20 | * @apiParam {Number} [offset] First element to return in the collection. 21 | * @apiParam {Number} [limit=500] Maximum number of elements to return. 22 | * @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. 23 | * @apiParam {String} [search] Looks for elements with the specified string. 24 | * @apiParam {String} [path] Filters by path. 25 | * 26 | * @apiDescription Returns the content of all CDB lists. 27 | * 28 | * @apiExample {curl} Example usage: 29 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/lists?pretty&path=etc/lists/audit-keys" 30 | * 31 | */ 32 | router.get('/', cache(), function(req, res) { 33 | logger.debug(req.connection.remoteAddress + " GET /lists"); 34 | 35 | req.apicacheGroup = "lists"; 36 | 37 | var data_request = {'function': '/lists', 'arguments': {}}; 38 | var filters = {'offset': 'numbers', 'limit': 'numbers', 'sort':'sort_param', 'search':'search_param', 39 | 'path': 'paths'}; 40 | 41 | if (!filter.check(req.query, filters, req, res)) // Filter with error 42 | return; 43 | 44 | if ('offset' in req.query) 45 | data_request['arguments']['offset'] = Number(req.query.offset); 46 | if ('limit' in req.query) 47 | data_request['arguments']['limit'] = Number(req.query.limit); 48 | if ('sort' in req.query) 49 | data_request['arguments']['sort'] = filter.sort_param_to_json(req.query.sort); 50 | if ('search' in req.query) 51 | data_request['arguments']['search'] = filter.search_param_to_json(req.query.search); 52 | if ('path' in req.query) 53 | data_request['arguments']['path'] = req.query.path; 54 | 55 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 56 | }) 57 | 58 | /** 59 | * @api {get} /lists/files Get paths from all lists 60 | * @apiName GetListPath 61 | * @apiGroup Info 62 | * 63 | * @apiParam {Number} [offset] First element to return in the collection. 64 | * @apiParam {Number} [limit=500] Maximum number of elements to return. 65 | * @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. 66 | * @apiParam {String} [search] Looks for elements with the specified string. 67 | * 68 | * @apiDescription Returns the path from all lists. 69 | * 70 | * @apiExample {curl} Example usage: 71 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/lists/files?pretty" 72 | * 73 | */ 74 | router.get('/files', cache(), function(req, res) { 75 | logger.debug(req.connection.remoteAddress + " GET /lists/files"); 76 | 77 | req.apicacheGroup = "lists"; 78 | 79 | var data_request = {'function': '/lists/files', 'arguments': {}}; 80 | 81 | var filters = {'offset': 'numbers', 'limit': 'numbers', 'sort':'sort_param', 'search':'search_param'}; 82 | 83 | if (!filter.check(req.query, filters, req, res)) // Filter with error 84 | return; 85 | 86 | if ('offset' in req.query) 87 | data_request['arguments']['offset'] = Number(req.query.offset); 88 | if ('limit' in req.query) 89 | data_request['arguments']['limit'] = Number(req.query.limit); 90 | if ('sort' in req.query) 91 | data_request['arguments']['sort'] = filter.sort_param_to_json(req.query.sort); 92 | if ('search' in req.query) 93 | data_request['arguments']['search'] = filter.search_param_to_json(req.query.search); 94 | 95 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 96 | }) 97 | 98 | module.exports = router; 99 | -------------------------------------------------------------------------------- /controllers/mitre.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2019 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | 13 | var router = require('express').Router(); 14 | 15 | /** 16 | * @api {get} /mitre Get information from Mitre database 17 | * @apiName GetMitre 18 | * @apiGroup Info 19 | * 20 | * @apiParam {Number} [offset] First element to return in the collection. 21 | * @apiParam {Number} [limit=10] Maximum number of elements to return. 22 | * @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. 23 | * @apiParam {String} [select] List of selected fields separated by commas. 24 | * @apiParam {String} [q] Query to filter results by. For example q="id=T1010" 25 | * @apiParam {String} [id] Filter by attack ID. 26 | * @apiParam {String} [phase_name] Filter by phase name. 27 | * @apiParam {String} [platform_name] Filter by platform name. 28 | * @apiParam {String} [search] Looks for elements with the specified string. 29 | * 30 | * @apiDescription Returns information from Mitre database 31 | * 32 | * @apiExample {curl} Example usage*: 33 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/mitre?limit=2&offset=4&pretty" 34 | * 35 | */ 36 | router.get('/', cache(), function(req, res) { 37 | logger.debug(req.connection.remoteAddress + " GET /mitre"); 38 | 39 | req.apicacheGroup = "mitre"; 40 | 41 | var data_request = {'function': '/mitre', 'arguments': {}}; 42 | var filters = {'offset': 'numbers', 'limit': 'numbers', 'q': 'query_param', 43 | 'id': 'search_param', 'phase_name': 'search_param', 44 | 'platform_name': 'names', 'search': 'search_param', 'sort':'sort_param', 'select': 'select_param'}; 45 | 46 | if (!filter.check(req.query, filters, req, res)) // Filter with error 47 | return; 48 | 49 | if ('offset' in req.query) 50 | data_request['arguments']['offset'] = Number(req.query.offset); 51 | if ('limit' in req.query) 52 | data_request['arguments']['limit'] = Number(req.query.limit); 53 | if ('id' in req.query) 54 | data_request['arguments']['id'] = req.query.id; 55 | if ('phase_name' in req.query) 56 | data_request['arguments']['phase_name'] = req.query.phase_name; 57 | if ('platform_name' in req.query) 58 | data_request['arguments']['platform_name'] = req.query.platform_name; 59 | if ('search' in req.query) 60 | data_request['arguments']['search'] = filter.search_param_to_json(req.query.search); 61 | if ('sort' in req.query) 62 | data_request['arguments']['sort'] = filter.sort_param_to_json(req.query.sort); 63 | if ('select' in req.query) 64 | data_request['arguments']['select'] = filter.select_param_to_json(req.query.select); 65 | if ('q' in req.query) 66 | data_request['arguments']['q'] = req.query.q; 67 | 68 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 69 | }) 70 | 71 | module.exports = router; 72 | -------------------------------------------------------------------------------- /controllers/rootcheck.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | 13 | var router = require('express').Router(); 14 | 15 | /** 16 | * @api {get} /rootcheck/:agent_id Get rootcheck database 17 | * @apiName GetRootcheckAgent 18 | * @apiGroup Info 19 | * 20 | * @apiParam {Number} agent_id Agent ID. 21 | * @apiParam {String} [pci] Filters by pci requirement. 22 | * @apiParam {String} [cis] Filters by CIS. 23 | * @apiParam {Number} [offset] First element to return in the collection. 24 | * @apiParam {Number} [limit=500] Maximum number of elements to return. 25 | * @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. 26 | * @apiParam {String} [search] Looks for elements with the specified string. 27 | * @apiParam {String} [status] Filters by status. 28 | * 29 | * @apiDescription Returns the rootcheck database of an agent. 30 | * 31 | * @apiExample {curl} Example usage: 32 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/rootcheck/000?offset=0&limit=2&pretty" 33 | * 34 | */ 35 | router.get('/:agent_id', cache(), function(req, res) { 36 | query_checks = {'status':'names', 'cis':'alphanumeric_param', 'pci':'alphanumeric_param'}; 37 | templates.array_request("/rootcheck/:agent_id", req, res, "rootcheck", {'agent_id':'numbers'}, query_checks); 38 | }) 39 | 40 | /** 41 | * @api {get} /rootcheck/:agent_id/pci Get rootcheck pci requirements 42 | * @apiName GetRootcheckAgentPCI 43 | * @apiGroup Info 44 | * 45 | * @apiParam {Number} [offset] First element to return in the collection. 46 | * @apiParam {Number} [limit=500] Maximum number of elements to return. 47 | * @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. 48 | * @apiParam {String} [search] Looks for elements with the specified string. 49 | * 50 | * @apiDescription Returns the PCI requirements of all rootchecks of the agent. 51 | * 52 | * @apiExample {curl} Example usage: 53 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/rootcheck/000/pci?offset=0&limit=10&pretty" 54 | * 55 | */ 56 | router.get('/:agent_id/pci', cache(), function(req, res) { 57 | templates.single_field_array_request("/rootcheck/:agent_id/pci", req, res, "rootcheck", 58 | {'agent_id':'numbers'}, {}); 59 | }) 60 | 61 | /** 62 | * @api {get} /rootcheck/:agent_id/cis Get rootcheck CIS requirements 63 | * @apiName GetRootcheckAgentCIS 64 | * @apiGroup Info 65 | * 66 | * @apiParam {Number} [offset] First element to return in the collection. 67 | * @apiParam {Number} [limit=500] Maximum number of elements to return. 68 | * @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. 69 | * @apiParam {String} [search] Looks for elements with the specified string. 70 | * 71 | * @apiDescription Returns the CIS requirements of all rootchecks of the specified agent. 72 | * 73 | * @apiExample {curl} Example usage: 74 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/rootcheck/000/cis?offset=0&limit=10&pretty" 75 | * 76 | */ 77 | router.get('/:agent_id/cis', cache(), function(req, res) { 78 | templates.single_field_array_request("/rootcheck/:agent_id/cis", req, res, "rootcheck", 79 | {'agent_id':'numbers'}, {}); 80 | }) 81 | 82 | /** 83 | * @api {get} /rootcheck/:agent_id/last_scan Get last rootcheck scan 84 | * @apiName GetRootcheckAgentLastScan 85 | * @apiGroup Info 86 | * 87 | * @apiParam {Number} agent_id Agent ID. 88 | * 89 | * @apiDescription Returns the timestamp of the last rootcheck scan. 90 | * 91 | * @apiExample {curl} Example usage: 92 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/rootcheck/000/last_scan?pretty" 93 | * 94 | */ 95 | router.get('/:agent_id/last_scan', cache(), function(req, res) { 96 | logger.debug(req.connection.remoteAddress + " GET /rootcheck/:agent_id/last_scan"); 97 | 98 | req.apicacheGroup = "rootcheck"; 99 | 100 | var data_request = {'function': '/rootcheck/:agent_id/last_scan', 'arguments': {}}; 101 | 102 | if (!filter.check(req.params, {'agent_id':'numbers'}, req, res)) // Filter with error 103 | return; 104 | 105 | data_request['arguments']['agent_id'] = req.params.agent_id; 106 | 107 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 108 | }) 109 | 110 | 111 | /** 112 | * @api {put} /rootcheck Run rootcheck scan in all agents 113 | * @apiName PutRootcheck 114 | * @apiGroup Run 115 | * 116 | * 117 | * @apiDescription Runs syscheck and rootcheck on all agents (Wazuh launches both processes simultaneously). 118 | * 119 | * @apiExample {curl} Example usage*: 120 | * curl -u foo:bar -k -X PUT "https://127.0.0.1:55000/rootcheck?pretty" 121 | * 122 | */ 123 | router.put('/', function(req, res) { 124 | logger.debug(req.connection.remoteAddress + " PUT /rootcheck"); 125 | 126 | var data_request = {'function': 'PUT/rootcheck', 'arguments': {}}; 127 | data_request['arguments']['all_agents'] = 1; 128 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 129 | }) 130 | 131 | /** 132 | * @api {put} /rootcheck/:agent_id Run rootcheck scan in an agent 133 | * @apiName PutRootcheckAgentId 134 | * @apiGroup Run 135 | * 136 | * @apiParam {Number} agent_id Agent ID. 137 | * 138 | * @apiDescription Runs syscheck and rootcheck on a specified agent (Wazuh launches both processes simultaneously) 139 | * 140 | * @apiExample {curl} Example usage: 141 | * curl -u foo:bar -k -X PUT "https://127.0.0.1:55000/rootcheck/000?pretty" 142 | * 143 | */ 144 | router.put('/:agent_id', function(req, res) { 145 | logger.debug(req.connection.remoteAddress + " PUT /rootcheck/:agent_id"); 146 | 147 | var data_request = {'function': 'PUT/rootcheck', 'arguments': {}}; 148 | 149 | if (!filter.check(req.params, {'agent_id':'numbers'}, req, res)) // Filter with error 150 | return; 151 | 152 | data_request['arguments']['agent_id'] = req.params.agent_id; 153 | 154 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 155 | }) 156 | 157 | 158 | /** 159 | * @api {delete} /rootcheck Clear rootcheck database 160 | * @apiName DeleteRootcheck 161 | * @apiGroup Clear 162 | * 163 | * 164 | * @apiDescription Clears the rootcheck database for all agents. 165 | * 166 | * @apiExample {curl} Example usage*: 167 | * curl -u foo:bar -k -X DELETE "https://127.0.0.1:55000/rootcheck?pretty" 168 | * 169 | */ 170 | router.delete('/', function(req, res) { 171 | logger.debug(req.connection.remoteAddress + " DELETE /rootcheck"); 172 | 173 | apicache.clear("rootcheck"); 174 | 175 | var data_request = {'function': 'DELETE/rootcheck', 'arguments': {}}; 176 | data_request['arguments']['all_agents'] = 1; 177 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 178 | }) 179 | 180 | /** 181 | * @api {delete} /rootcheck/:agent_id Clear rootcheck database of an agent 182 | * @apiName DeleteRootcheckAgentId 183 | * @apiGroup Clear 184 | * 185 | * @apiParam {Number} agent_id Agent ID. 186 | * 187 | * @apiDescription Clears the rootcheck database for a specific agent. 188 | * 189 | * @apiExample {curl} Example usage*: 190 | * curl -u foo:bar -k -X DELETE "https://127.0.0.1:55000/rootcheck/000?pretty" 191 | * 192 | */ 193 | router.delete('/:agent_id', function(req, res) { 194 | logger.debug(req.connection.remoteAddress + " DELETE /rootcheck/:agent_id"); 195 | 196 | apicache.clear("rootcheck"); 197 | 198 | var data_request = {'function': 'DELETE/rootcheck', 'arguments': {}}; 199 | 200 | if (!filter.check(req.params, {'agent_id':'numbers'}, req, res)) // Filter with error 201 | return; 202 | 203 | data_request['arguments']['agent_id'] = req.params.agent_id; 204 | 205 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 206 | }) 207 | 208 | 209 | 210 | module.exports = router; 211 | -------------------------------------------------------------------------------- /controllers/security_configuration_assessment.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | 13 | var router = require('express').Router(); 14 | 15 | /** 16 | * @api {get} /sca/:agent_id Get security configuration assessment (SCA) database 17 | * @apiName GetSCAAgent 18 | * @apiGroup Info 19 | * 20 | * @apiParam {Number} agent_id Agent ID. 21 | * @apiParam {String} [name] Filters by policy name. 22 | * @apiParam {String} [description] Filters by policy description 23 | * @apiParam {String} [references] Filters by references 24 | * @apiParam {Number} [offset] First element to return in the collection. 25 | * @apiParam {Number} [limit=500] Maximum number of elements to return. 26 | * @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. 27 | * @apiParam {String} [search] Looks for elements with the specified string. 28 | * @apiParam {String} [q] Query to filter results by. This is specially useful to filter by total checks passed, failed or total score (fields pass, fail, score). 29 | * 30 | * @apiDescription Returns the sca database of an agent. 31 | * 32 | * @apiExample {curl} Example usage: 33 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/sca/000?q=pass>2;score<150&pretty&limit=2" 34 | * 35 | */ 36 | router.get('/:agent_id', cache(), function(req, res) { 37 | query_checks = {'name':'alphanumeric_param', 'description': 'alphanumeric_param', 'references': 'encoded_uri'}; 38 | templates.array_request("/sca/:agent_id", req, res, "sca", {'agent_id': 'numbers'}, query_checks); 39 | }) 40 | 41 | 42 | /** 43 | * @api {get} /sca/:agent_id/checks/:policy_id Get security configuration assessment (SCA) checks database 44 | * @apiName GetSCAAgentChecks 45 | * @apiGroup Info 46 | * 47 | * @apiParam {Number} [agent_id] Agent ID. 48 | * @apiParam {String} [policy_id] Filters by policy id 49 | * @apiParam {String} [title] Filters by title 50 | * @apiParam {String} [description] Filters by policy description 51 | * @apiParam {String} [rationale] Filters by rationale 52 | * @apiParam {String} [remediation] Filters by remediation 53 | * @apiParam {String} [file] Filters by file 54 | * @apiParam {String} [process] Filters by process 55 | * @apiParam {String} [directory] Filters by directory 56 | * @apiParam {String} [registry] Filters by registry 57 | * @apiParam {String} [references] Filters by references 58 | * @apiParam {String} [result] Filters by result 59 | * @apiParam {String} [condition] Filters by condition 60 | * @apiParam {Number} [offset] First element to return in the collection. 61 | * @apiParam {Number} [limit=500] Maximum number of elements to return. 62 | * @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. 63 | * @apiParam {String} [search] Looks for elements with the specified string. 64 | * @apiParam {String} [command] Looks for elements with the specified command. 65 | * @apiParam {String} [status] Looks for elements with the specified status. 66 | * @apiParam {String} [reason] Looks for elements with the specified reason. 67 | * 68 | * @apiDescription Returns the sca checks of an agent. 69 | * 70 | * @apiExample {curl} Example usage: 71 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/sca/000/checks/unix_audit?limit=1&pretty" 72 | * 73 | */ 74 | router.get('/:agent_id/checks/:policy_id', cache(), function(req, res) { 75 | query_checks = {'title': 'symbols_alphanumeric_param', 'description': 'symbols_alphanumeric_param', 76 | 'rationale': 'symbols_alphanumeric_param', 'remediation': 'symbols_alphanumeric_param', 77 | 'file': 'paths', 'process': 'alphanumeric_param', 'directory': 'paths', 78 | 'registry': 'alphanumeric_param', 'references': 'encoded_uri', 79 | 'result': 'alphanumeric_param', 'condition': 'alphanumeric_param', 'command': 'alphanumeric_param', 80 | 'status': 'alphanumeric_param', 'reason': 'symbols_alphanumeric_param' 81 | }; 82 | templates.array_request("/sca/:agent_id/checks/:policy_id", req, res, 83 | "sca", 84 | {'agent_id': 'numbers', 'policy_id': 'alphanumeric_param'}, query_checks); 85 | }) 86 | 87 | module.exports = router; 88 | -------------------------------------------------------------------------------- /controllers/summary.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | 13 | var router = require('express').Router(); 14 | 15 | 16 | /** 17 | * @api {get} /summary/agents Get a full summary of agents 18 | * @apiName GetSummaryAgents 19 | * @apiGroup Info 20 | * 21 | * @apiDescription Returns a dictionary with a full summary of agents. 22 | * 23 | * @apiExample {curl} Example usage: 24 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/summary/agents?pretty" 25 | * 26 | */ 27 | router.get('/agents', cache(), function(req, res) { 28 | logger.debug(req.connection.remoteAddress + " GET /summary/agents"); 29 | 30 | req.apicacheGroup = "summary"; 31 | 32 | var data_request = {'function': '/summary/agents', 'arguments': {}}; 33 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 34 | }) 35 | 36 | module.exports = router; 37 | -------------------------------------------------------------------------------- /controllers/syscheck.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | 13 | var router = require('express').Router(); 14 | 15 | 16 | /** 17 | * @api {get} /syscheck/:agent_id Get syscheck files 18 | * @apiName GetSyscheckAgent 19 | * @apiGroup Info 20 | * 21 | * @apiParam {Number} agent_id Agent ID. 22 | * @apiParam {Number} [offset] First element to return in the collection. 23 | * @apiParam {Number} [limit=500] Maximum number of elements to return. 24 | * @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. 25 | * @apiParam {String} [search] Looks for elements with the specified string. 26 | * @apiParam {String} [file] Filters file by filename. 27 | * @apiParam {String="file","registry"} [type] Selects type of file. 28 | * @apiParam {String="yes", "no"} [summary] Returns a summary grouping by filename. 29 | * @apiParam {String} [select] List of selected fields separated by commas. 30 | * @apiParam {Empty_Boolean} [distinct] Whether to return only distinct values or not. 31 | * @apiParam {String} [md5] Returns the files with the specified md5 hash. 32 | * @apiParam {String} [sha1] Returns the files with the specified sha1 hash. 33 | * @apiParam {String} [sha256] Returns the files with the specified sha256 hash. 34 | * @apiParam {String} [hash] Returns the files with the specified hash (md5, sha1 or sha256). 35 | * @apiParam {string} [q] Advanced query filtering 36 | * 37 | * @apiDescription Returns the syscheck files of an agent. 38 | * 39 | * @apiExample {curl} Example usage: 40 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/syscheck/000?offset=0&limit=2&pretty" 41 | * 42 | */ 43 | router.get('/:agent_id', cache(), function(req, res) { 44 | var filters = {'file':'paths', 'type':'names', 'summary':'yes_no_boolean', 45 | 'select': 'alphanumeric_param','md5':'hashes', 'sha1':'hashes', 46 | 'sha256': 'hashes', 'hash':'hashes'}; 47 | templates.array_request("/syscheck/:agent_id", req, res, "syscheck", {'agent_id': 'numbers'}, filters); 48 | }); 49 | 50 | 51 | /** 52 | * @api {get} /syscheck/:agent_id/last_scan Get last syscheck scan 53 | * @apiName GetSyscheckAgentLastScan 54 | * @apiGroup Info 55 | * 56 | * @apiParam {Number} agent_id Agent ID. 57 | * 58 | * @apiDescription Return the timestamp of the last syscheck scan. 59 | * 60 | * @apiExample {curl} Example usage: 61 | * curl -u foo:bar -k -X GET "https://127.0.0.1:55000/syscheck/000/last_scan?pretty" 62 | * 63 | */ 64 | router.get('/:agent_id/last_scan', cache(), function(req, res) { 65 | logger.debug(req.connection.remoteAddress + " GET /syscheck/:agent_id/last_scan"); 66 | 67 | req.apicacheGroup = "syscheck"; 68 | 69 | var data_request = {'function': '/syscheck/:agent_id/last_scan', 'arguments': {}}; 70 | 71 | if (!filter.check(req.params, {'agent_id':'numbers'}, req, res)) // Filter with error 72 | return; 73 | data_request['arguments']['agent_id'] = req.params.agent_id; 74 | 75 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 76 | }) 77 | 78 | 79 | /** 80 | * @api {put} /syscheck Run syscheck scan in all agents 81 | * @apiName PutSyscheck 82 | * @apiGroup Run 83 | * 84 | * 85 | * @apiDescription Runs syscheck and rootcheck on all agents (Wazuh launches both processes simultaneously). 86 | * 87 | * @apiExample {curl} Example usage*: 88 | * curl -u foo:bar -k -X PUT "https://127.0.0.1:55000/syscheck?pretty" 89 | * 90 | */ 91 | router.put('/', function(req, res) { 92 | logger.debug(req.connection.remoteAddress + " PUT /syscheck"); 93 | 94 | var data_request = {'function': 'PUT/syscheck', 'arguments': {}}; 95 | data_request['arguments']['all_agents'] = 1; 96 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 97 | }) 98 | 99 | /** 100 | * @api {put} /syscheck/:agent_id Run syscheck scan in an agent 101 | * @apiName PutSyscheckAgentId 102 | * @apiGroup Run 103 | * 104 | * @apiParam {Number} agent_id Agent ID. 105 | * 106 | * @apiDescription Runs syscheck and rootcheck on an agent (Wazuh launches both processes simultaneously). 107 | * 108 | * @apiExample {curl} Example usage: 109 | * curl -u foo:bar -k -X PUT "https://127.0.0.1:55000/syscheck/000?pretty" 110 | * 111 | */ 112 | router.put('/:agent_id', function(req, res) { 113 | logger.debug(req.connection.remoteAddress + " PUT /syscheck/:agent_id"); 114 | 115 | var data_request = {'function': 'PUT/syscheck', 'arguments': {}}; 116 | 117 | if (!filter.check(req.params, {'agent_id':'numbers'}, req, res)) // Filter with error 118 | return; 119 | data_request['arguments']['agent_id'] = req.params.agent_id; 120 | 121 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 122 | }) 123 | 124 | /** 125 | * @api {delete} /syscheck/:agent_id Clear syscheck database of an agent 126 | * @apiName DeleteSyscheckAgentId 127 | * @apiGroup Clear 128 | * 129 | * @apiParam {Number} agent_id Agent ID. 130 | * 131 | * @apiDescription Clears the syscheck database for the specified agent. 132 | * 133 | * @apiExample {curl} Example usage*: 134 | * curl -u foo:bar -k -X DELETE "https://127.0.0.1:55000/syscheck/000?pretty" 135 | * 136 | */ 137 | router.delete('/:agent_id', function(req, res) { 138 | logger.debug(req.connection.remoteAddress + " DELETE /syscheck/:agent_id"); 139 | 140 | apicache.clear("syscheck"); 141 | 142 | var data_request = {'function': 'DELETE/syscheck/:agent_id', 'arguments': {}}; 143 | 144 | if (!filter.check(req.params, {'agent_id':'numbers'}, req, res)) // Filter with error 145 | return; 146 | data_request['arguments']['agent_id'] = req.params.agent_id; 147 | 148 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 149 | }) 150 | 151 | 152 | 153 | module.exports = router; 154 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # Generating Wazuh API Reference documentation 2 | 3 | ## Environment 4 | It is necessary to set up a Docker environment for generating the API documentation. There are Docker environments in this repository. Please, follow the next steps for setting up a Docker environment: 5 | ```shellsession 6 | $ cd {WAZUH_API_PATH}/test/environment/docker/centos 7 | $ docker-compose up -d 8 | $ docker exec -it centos_wazuh-master_1 bash 9 | ``` 10 | 11 | ## Generate documentation: 12 | 13 | Inside the container, execute the following commands and the `.rst` file will be generated: 14 | ```shellsession 15 | # cd /wazuh-api/doc 16 | # ./generate_api_doc.sh 17 | # cd /wazuh-documentation 18 | # make html 19 | ``` 20 | 21 | Review **/wazuh-documentation/source/user-manual/api/reference.rst**, specially *Example Response* section. 22 | -------------------------------------------------------------------------------- /examples/api-client.ps1: -------------------------------------------------------------------------------- 1 | ### 2 | # API RESTful for OSSEC 3 | # Copyright (C) 2015-2016 Wazuh, Inc.All rights reserved. 4 | # Wazuh.com 5 | # 6 | # This program is a free software; you can redistribute it 7 | # and/or modify it under the terms of the GNU General Public 8 | # License (version 2) as published by the FSF - Free Software 9 | # Foundation. 10 | ### 11 | 12 | # How to use OSSEC Wazuh RESTful API from PowerShell 3.0+ 13 | # Documentation: https://documentation.wazuh.com/current/user-manual/api/index.html 14 | 15 | function Ignore-SelfSignedCerts { 16 | add-type @" 17 | using System.Net; 18 | using System.Security.Cryptography.X509Certificates; 19 | 20 | public class PolicyCert : ICertificatePolicy { 21 | public PolicyCert() {} 22 | public bool CheckValidationResult( 23 | ServicePoint sPoint, X509Certificate cert, 24 | WebRequest wRequest, int certProb) { 25 | return true; 26 | } 27 | } 28 | "@ 29 | [System.Net.ServicePointManager]::CertificatePolicy = new-object PolicyCert 30 | } 31 | 32 | function req($method, $resource){ 33 | $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username, $password))) 34 | $url = $base_url + $resource; 35 | 36 | try{ 37 | return Invoke-RestMethod -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method $method -Uri $url 38 | }catch{ 39 | return $_.Exception 40 | } 41 | 42 | } 43 | 44 | # Configuration 45 | $base_url = "https://IP:55000" 46 | $username = "foo" 47 | $password = "bar" 48 | Ignore-SelfSignedCerts 49 | 50 | #Requests 51 | Write-Output "Welcome:" 52 | $response = req -method "get" -resource "/" 53 | Write-Output $response 54 | 55 | Write-Output "`r`n`r`nAgents:" 56 | $response = req -method "get" -resource "/agents" 57 | Write-Output $response 58 | 59 | Write-Output "`r`n`r`nManager:" 60 | $response = req -method "get" -resource "/manager/status" 61 | Write-Output $response 62 | 63 | Write-Output "`r`n`r`nWazuh.com" 64 | -------------------------------------------------------------------------------- /examples/api-client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ### 3 | # API RESTful for OSSEC 4 | # Copyright (C) 2015-2016 Wazuh, Inc.All rights reserved. 5 | # Wazuh.com 6 | # 7 | # This program is a free software; you can redistribute it 8 | # and/or modify it under the terms of the GNU General Public 9 | # License (version 2) as published by the FSF - Free Software 10 | # Foundation. 11 | ### 12 | 13 | # How to use OSSEC Wazuh RESTful API from Python 14 | # Requests module: http://docs.python-requests.org/ 15 | # Documentation: https://documentation.wazuh.com/current/user-manual/api/index.html 16 | 17 | import json 18 | import sys 19 | try: 20 | import requests 21 | from requests.auth import HTTPBasicAuth 22 | except Exception as e: 23 | print("No module 'requests' found. Install: pip install requests") 24 | sys.exit() 25 | 26 | 27 | def req(method, resource, data=None): 28 | url = '{0}/{1}'.format(base_url, resource) 29 | 30 | try: 31 | requests.packages.urllib3.disable_warnings() 32 | 33 | if method.lower() == 'post': 34 | r = requests.post(url, auth=auth, data=data, verify=verify) 35 | elif method.lower() == 'put': 36 | r = requests.put(url, auth=auth, data=data, verify=verify) 37 | elif method.lower() == 'delete': 38 | r = requests.delete(url, auth=auth, data=data, verify=verify) 39 | else: 40 | r = requests.get(url, auth=auth, params=data, verify=verify) 41 | 42 | code = r.status_code 43 | res_json = r.json() 44 | 45 | except Exception as exception: 46 | print("Error: {0}".format(exception)) 47 | sys.exit(1) 48 | 49 | return code, res_json 50 | 51 | 52 | def code_desc(http_status_code): 53 | return requests.status_codes._codes[http_status_code][0] 54 | 55 | if __name__ == "__main__": 56 | # Configuration 57 | base_url = 'https://localhost:55000' 58 | auth = HTTPBasicAuth('foo', 'bar') 59 | verify = False # Use with self-signed certificates. 60 | 61 | print("Welcome:") 62 | status_code, response = req('get', '/') 63 | print(json.dumps(response, indent=4, sort_keys=True)) 64 | print("Status: {0} - {1}".format(status_code, code_desc(status_code))) 65 | 66 | print("\nAgents:") 67 | status_code, response = req('get', '/agents') 68 | print(json.dumps(response, indent=4, sort_keys=True)) 69 | print("Status: {0} - {1}".format(status_code, code_desc(status_code))) 70 | 71 | print("\nManager:") 72 | status_code, response = req('get', '/manager/status') 73 | print(json.dumps(response, indent=4, sort_keys=True)) 74 | print("Status: {0} - {1}".format(status_code, code_desc(status_code))) 75 | 76 | print("\n\nWazuh.com") 77 | -------------------------------------------------------------------------------- /examples/api-register-agent.ps1: -------------------------------------------------------------------------------- 1 | 2 | ### 3 | # Powershell script for registering agents automatically with the API 4 | # Copyright (C) 2017 Wazuh, Inc. All rights reserved. 5 | # Wazuh.com 6 | # 7 | # This program is a free software; you can redistribute it 8 | # and/or modify it under the terms of the GNU General Public 9 | # License (version 2) as published by the FSF - Free Software 10 | # Foundation. 11 | ### 12 | 13 | function Ignore-SelfSignedCerts { 14 | add-type @" 15 | using System.Net; 16 | using System.Security.Cryptography.X509Certificates; 17 | public class PolicyCert : ICertificatePolicy { 18 | public PolicyCert() {} 19 | public bool CheckValidationResult( 20 | ServicePoint sPoint, X509Certificate cert, 21 | WebRequest wRequest, int certProb) { 22 | return true; 23 | } 24 | } 25 | "@ 26 | [System.Net.ServicePointManager]::CertificatePolicy = new-object PolicyCert 27 | [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12; 28 | } 29 | 30 | function req($method, $resource, $params){ 31 | $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username, $password))) 32 | $url = $base_url + $resource; 33 | 34 | try{ 35 | return Invoke-WebRequest -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method $method -Uri $url -Body $params 36 | }catch{ 37 | return $_.Exception 38 | } 39 | 40 | } 41 | 42 | # Configuration 43 | $base_url = "http://:55000" 44 | $username = "foo" 45 | $password = "bar" 46 | $agent_name = $env:computername 47 | $path = "C:\Program Files (x86)\ossec-agent\" 48 | $config = "C:\Program Files (x86)\ossec-agent\ossec.conf" 49 | $wazuh_manager = "" 50 | Ignore-SelfSignedCerts 51 | 52 | # Test API integration to make sure IE has run through initial startup dialogue - This can be a problem with new servers. 53 | 54 | try{ 55 | $testresponse = req -method "GET" -resource "/manager/info?pretty" | ConvertFrom-Json | select -expand data -ErrorAction Stop -ErrorVariable geterr 56 | 57 | Write-Output "The Wazuh manager is contactable via the API, the response is: `n $($testresponse)" 58 | }catch{ 59 | Write-Host -ForegroundColor Red "IE has not had it's initial startup dialogue dismissed, please complete this step and try again. Script will exit. Error: $($geterr)`n .Please Run OSSEC_AgentConfig Separately once you correct the error." 60 | Exit 61 | } 62 | 63 | # Test for agent already existing in manager 64 | 65 | $agentexist = req -method "GET" -resource "/agents?pretty" -params @{search=$agent_name} # searches for the agent based on the env variable name 66 | 67 | $agentinfo = $agentexist.Content | ConvertFrom-Json | select -expand data | select totalitems 68 | 69 | $agentexistid = $agentexist.Content | ConvertFrom-Json | select -expand data | select -expand items | select id # expands the embedded JSON items to retrieve the agent ID 70 | 71 | # If agent does not already exist proceed to create agent and register the agent key 72 | 73 | if ($agentinfo.totalitems -lt 1){ 74 | 75 | # Adding agent and getting Id from manager 76 | 77 | Write-Output "`r`nAdding agent:" 78 | $response = req -method "POST" -resource "/agents" -params @{name=$agent_name} | ConvertFrom-Json 79 | If ($response.error -ne '0') { 80 | Write-Output "ERROR: $($response.message)" 81 | Exit 82 | } 83 | $agent_id = $response.data 84 | Write-Output "Agent '$($agent_name)' with ID '$($agent_id)' added." 85 | 86 | # Getting agent key from manager 87 | 88 | Write-Output "`r`nGetting agent key:" 89 | $response = req -method "GET" -resource "/agents/$($agent_id)/key" | ConvertFrom-Json 90 | If ($response.error -ne '0') { 91 | Write-Output "ERROR: $($response.message)" 92 | Exit 93 | } 94 | $agent_key = $response.data 95 | Write-Output "Key for agent '$($agent_id)' received." 96 | 97 | # Importing key 98 | 99 | Write-Output "`r`nImporting authentication key:" 100 | echo "y" | & "$($path)manage_agents.exe" "-i $($agent_key)" "y`r`n" 101 | 102 | # Restarting agent 103 | 104 | Write-Output "`r`nRestarting:" 105 | $srvName = "OssecSvc" 106 | 107 | Write-Output "Stopping service." 108 | Stop-Service $srvName 109 | $srvStat = Get-Service $srvName 110 | Write-Output "$($srvName) is now $($srvStat.status)" 111 | 112 | Start-Sleep -s 10 113 | 114 | Add-Content $config "`n
$($wazuh_manager)
" 115 | 116 | Start-Sleep -s 10 117 | 118 | Write-Output "Starting service." 119 | Start-Service $srvName 120 | $srvStat = Get-Service $srvName 121 | Write-Output "$($srvName) is now $($srvStat.status)" 122 | } 123 | Else{ 124 | 125 | # If agent is found in manager by name it will retrieve the key and configure the agent 126 | 127 | $response = req -method "GET" -resource "/agents/$($agentexistid.id)/key" | ConvertFrom-Json 128 | # Key received from manager 129 | $agent_key = $response.data 130 | # Importing agent key from manager 131 | Write-Output "`r`nImporting authentication key:" 132 | echo "y" | & "$($path)manage_agents.exe" "-i $($agent_key)" "y`r`n" 133 | 134 | Write-Output "`r`nRestarting:" 135 | $srvName = "OssecSvc" 136 | 137 | Write-Output "Stopping service." 138 | Stop-Service $srvName 139 | $srvStat = Get-Service $srvName 140 | Write-Output "$($srvName) is now $($srvStat.status)" 141 | 142 | Start-Sleep -s 10 143 | 144 | Add-Content $config "`n
$($wazuh_manager)
" 145 | 146 | Start-Sleep -s 10 147 | 148 | Write-Output "Starting service." 149 | Start-Service $srvName 150 | $srvStat = Get-Service $srvName 151 | Write-Output "$($srvName) is now $($srvStat.status)" 152 | 153 | 154 | } 155 | -------------------------------------------------------------------------------- /examples/api-register-agent.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ### 4 | # Python script for registering agents automatically with the API 5 | # Copyright (C) 2017 Wazuh, Inc. All rights reserved. 6 | # Wazuh.com 7 | # 8 | # This program is a free software; you can redistribute it 9 | # and/or modify it under the terms of the GNU General Public 10 | # License (version 2) as published by the FSF - Free Software 11 | # Foundation. 12 | ### 13 | 14 | import os 15 | import json 16 | import sys 17 | from subprocess import PIPE, Popen 18 | try: 19 | import requests 20 | from requests.auth import HTTPBasicAuth 21 | except Exception as e: 22 | print("No module 'requests' found. Install: pip install requests") 23 | sys.exit() 24 | 25 | 26 | def req(method, resource, data=None): 27 | url = '{0}/{1}'.format(base_url, resource) 28 | 29 | try: 30 | requests.packages.urllib3.disable_warnings() 31 | 32 | if method.lower() == 'post': 33 | r = requests.post(url, auth=auth, data=data, verify=verify) 34 | elif method.lower() == 'put': 35 | r = requests.put(url, auth=auth, data=data, verify=verify) 36 | elif method.lower() == 'delete': 37 | r = requests.delete(url, auth=auth, data=data, verify=verify) 38 | else: 39 | r = requests.get(url, auth=auth, params=data, verify=verify) 40 | 41 | code = r.status_code 42 | res_json = r.json() 43 | 44 | except Exception as exception: 45 | print("Error: {0}".format(exception)) 46 | sys.exit(1) 47 | 48 | return code, res_json 49 | 50 | 51 | def code_desc(http_status_code): 52 | return requests.status_codes._codes[http_status_code][0] 53 | 54 | 55 | def add_agent(agt_name, agt_ip=None): 56 | if agt_ip: 57 | status_code, response = req('post', 'agents', {'name': agt_name, 'ip': agt_ip}) 58 | else: 59 | status_code, response = req('post', 'agents', {'name': agt_name}) 60 | 61 | if status_code == 200 and response['error'] == 0: 62 | r_id = response['data']['id'] 63 | r_key = response['data']['key'] 64 | return r_id, r_key 65 | else: 66 | msg = json.dumps(response, indent=4, sort_keys=True) 67 | code = "Status: {0} - {1}".format(status_code, code_desc(status_code)) 68 | exit("ERROR - ADD AGENT:\n{0}\n{1}".format(code, msg)) 69 | 70 | 71 | def import_key(agent_key): 72 | cmd = "/var/ossec/bin/manage_agents" 73 | std_out, std_err, r_code = execute([cmd, "-i", agent_key], "y\n\n") 74 | if r_code != 0: 75 | exit("ERROR - IMPORT KEY:{0}".format(std_err)) 76 | 77 | def get_hostname(): 78 | out, err, r_code = execute(["hostname"]) 79 | if r_code != 0: 80 | exit("ERROR: Hostname unknown: {0}".format(e)) 81 | 82 | hostname = out.strip() 83 | 84 | return hostname 85 | 86 | def execute(cmd_list, stdin=None): 87 | p = Popen(cmd_list, stdin=PIPE, stdout=PIPE, stderr=PIPE) 88 | std_out, std_err = p.communicate(stdin) 89 | return_code = p.returncode 90 | return std_out, std_err, return_code 91 | 92 | def restart_ossec(): 93 | cmd = "/var/ossec/bin/ossec-control" 94 | std_out, std_err, r_code = execute([cmd, "restart"]) 95 | restarted = False 96 | 97 | for line_output in std_out.split(os.linesep): 98 | if "Completed." in line_output: 99 | restarted = True 100 | break 101 | 102 | if not restarted: 103 | exit("ERROR - RESTARTING OSSEC:{0}".format(std_err)) 104 | 105 | if __name__ == "__main__": 106 | # Configuration 107 | base_url = 'http://10.0.0.1:55000' 108 | auth = HTTPBasicAuth('foo', 'bar') 109 | agent_name = "auto" 110 | verify = False # Use with self-signed certificates. 111 | 112 | print("Adding agent.") 113 | if agent_name == "auto": 114 | agent_name = get_hostname() 115 | 116 | agent_id, agent_key = add_agent(agent_name) 117 | print("Agent '{0}' with ID '{1}' added.".format(agent_name, agent_id)) 118 | 119 | print("Importing authentication key.") 120 | 121 | import_key(agent_key) 122 | 123 | print("Restarting.") 124 | 125 | restart_ossec() 126 | -------------------------------------------------------------------------------- /examples/api-register-agent.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### 4 | # Shell script for registering agents automatically with the API 5 | # Copyright (C) 2017 Wazuh, Inc. All rights reserved. 6 | # Wazuh.com 7 | # 8 | # This program is a free software; you can redistribute it 9 | # and/or modify it under the terms of the GNU General Public 10 | # License (version 2) as published by the FSF - Free Software 11 | # Foundation. 12 | ### 13 | 14 | 15 | # Connection variables 16 | API_IP="10.0.0.1" 17 | API_PORT="55000" 18 | PROTOCOL="http" 19 | USER="foo" 20 | PASSWORD="bar" 21 | 22 | display_help() { 23 | cat < agent is not registered 109 | if ! [ "$AGENT_ID" -eq "$AGENT_ID" ] 2> /dev/null ; then 110 | echo "Starting registration process ..." 111 | : 112 | elif [[ "$FORCE" = true && "$SILENT" = "true" ]] ; then 113 | remove_agent > /dev/null 2>&1 114 | else 115 | if [[ "$FORCE" = true ]] ; then 116 | remove_agent 117 | fi 118 | fi 119 | 120 | # Default action -> try to register the agent 121 | register_agent 122 | -------------------------------------------------------------------------------- /helpers/check.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh API RESTful 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | exports.configuration_file = function() { 13 | var config_fields = ['ossec_path', 'host', 'port', 'https', 'basic_auth', 'BehindProxyServer', 'logs', 'cors', 'cache_enabled', 'cache_debug', 'cache_time', 'log_path']; 14 | 15 | for (i = 0; i < config_fields.length; i++) { 16 | 17 | // Exist 18 | if (!(config_fields[i] in config)){ 19 | console.log("Configuration error: Element '" + config_fields[i] + "' not found. Exiting."); 20 | return -1; 21 | } 22 | 23 | if (config_fields[i] != "python"){ 24 | // String 25 | if (typeof config[config_fields[i]] !== "string") { 26 | console.log("Configuration error: Element '" + config_fields[i] + "' must be an string. Exiting."); 27 | return -1; 28 | } 29 | // Empty 30 | if (!config[config_fields[i]].trim()){ 31 | console.log("Configuration error: Element '" + config_fields[i] + "' is empty. Exiting."); 32 | return -1; 33 | } 34 | } 35 | } 36 | 37 | return 0; 38 | } 39 | 40 | 41 | // Check Wazuh version 42 | exports.wazuh = function(my_logger) { 43 | try { 44 | var fs = require("fs"); 45 | var wazuh_version_mayor = 0; 46 | var version_regex = new RegExp('VERSION="v(.+)"'); 47 | var wazuh_version = "v0"; 48 | 49 | fs.readFileSync('/etc/ossec-init.conf').toString().split('\n').forEach(function (line) { 50 | var match = line.match(version_regex); 51 | if (match) { 52 | wazuh_version = match[1] 53 | wazuh_version_mayor = parseInt(wazuh_version[0]); 54 | return; 55 | } 56 | }); 57 | 58 | // Wazuh 2.0 or newer required 59 | if (wazuh_version_mayor < 2) { 60 | if (wazuh_version_mayor == 0) 61 | var msg = "not"; 62 | else 63 | var msg = wazuh_version; 64 | 65 | var f_msg = "ERROR: Wazuh manager v" + msg + " found. It is required Wazuh manager v2.0.0 or newer. Exiting."; 66 | console.log(f_msg); 67 | my_logger.log(f_msg); 68 | return -1; 69 | } 70 | 71 | // Wazuh major.minor == API major.minor 72 | var wazuh_api_mm = info_package.version.substring(0, 3) 73 | if ( wazuh_version.substring(0, 3) != wazuh_api_mm ){ 74 | var f_msg = "ERROR: Wazuh manager v" + wazuh_version + " found. Wazuh manager v" + wazuh_api_mm + ".x expected. Exiting."; 75 | console.log(f_msg); 76 | my_logger.log(f_msg); 77 | return -1; 78 | } 79 | } catch (e) { 80 | var f_msg = "WARNING: The installed version of Wazuh manager could not be determined. It is required Wazuh Manager 2.0 or newer."; 81 | console.log(f_msg); 82 | my_logger.log(f_msg); 83 | } 84 | 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /helpers/errors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | var errors = {}; 13 | 14 | // 1 - 9 Internal Errors 15 | errors['1'] = "Error executing internal command"; 16 | errors['2'] = "Command output not in JSON"; 17 | errors['3'] = "Internal error"; 18 | 19 | // Auth 20 | errors['100'] = "Unauthorized request. Cluster privileges required"; 21 | 22 | // 600 - 699 Requests 23 | errors['600'] = "Param not valid. Integer expected"; // Integer 24 | errors['numbers'] = 600; 25 | errors['601'] = "Param not valid. Valid characters: a-z, A-Z, 0-9, ., _, -, %"; // Names 26 | errors['names'] = 601; 27 | errors['602'] = "Param not valid. Invalid command"; // Active response command 28 | errors['active_response_command'] = 602; 29 | errors['603'] = "The requested URL was not found on this server"; // Default error 30 | errors['604'] = "Filter error"; // Filter 31 | errors['605'] = "Param not valid. Date format: YYYYMMDD"; // Date 32 | errors['dates'] = 605; 33 | errors['606'] = "Param not valid. IP invalid"; // IP 34 | errors['ips'] = 606; // IP 35 | errors['607'] = "Invalid content-type. POST requests should be 'application/json', 'application/x-www-form-urlencoded', 'application/xml' or 'application/octet-stream'"; // 36 | errors['608'] = "Param not valid. Path invalid. Valid characters: a-z, A-Z, 0-9, ., _, -, :, /, \\"; // Paths 37 | errors['paths'] = 608; 38 | errors['609'] = "Param not valid. Valid characters: a-z, A-Z, 0-9, ., _, -, +"; // Alphanumeric params 39 | errors['alphanumeric_param'] = 609; 40 | errors['610'] = "Param not valid. Valid characters: number or interval separated by '-'"; // range 41 | errors['ranges'] = 610; 42 | errors['611'] = "Param not valid. Valid characters: a-z, A-Z, 0-9, _, -, +, ,"; // sort 43 | errors['sort_param'] = 611; 44 | errors['612'] = "Param not valid. Invalid characters: ; & | * ^ >"; // search 45 | errors['search_param'] = 612; 46 | errors['613'] = "Param not valid. Valid values: md5/sha1/sha256 hash"; // hashes 47 | errors['hashes'] = 613; 48 | errors['614'] = "Invalid request" 49 | errors['615'] = "Param not valid. Valid characters: a-z A-Z 0-9"; // keys 50 | errors['ossec_key'] = 615; 51 | errors['616'] = "Param not valid. Valid values: array of numbers"; 52 | errors['array_numbers'] = 616; 53 | errors['timeframe_type'] = 617; 54 | errors['617'] = "Param not valid. Valid characters: [0-9]d|[0-9]h|[0-9]m|[0-9]s|0-9"; 55 | errors['boolean'] = 618; 56 | errors['618'] = "Param not valid. Valid values: true or false"; 57 | errors['619'] = "Param not valid. Valid characters: a-z A-Z 0-9 space . , _"; // select 58 | errors['select_param'] = 619; 59 | errors['yes_no_boolean'] = 620; 60 | errors['620'] = "Param not valid. Valid values: yes or no"; 61 | errors['array_names'] = 621; 62 | errors['621'] = "Invalid character in parameters"; 63 | errors['query_param'] = 622; 64 | errors['622'] = 'Param not valid. Review queries documentation: https://documentation.wazuh.com/current/user-manual/api/queries.html' 65 | errors['empty_boolean'] = 623; 66 | errors['623'] = 'Param not valid. Valid values: true,false or empty(true)'; 67 | errors['array'] = 624; 68 | errors['624'] = 'Param not valid. Valid values: array'; 69 | errors['700'] = "File not found"; 70 | errors['701'] = "Size of XML file is too long"; 71 | errors['702'] = "Could not write XML temporary file"; 72 | errors['703'] = "Invalid XML file"; 73 | errors['704'] = "Invalid path"; 74 | errors['705'] = "Invalid CDB list. Format for CDB list is 'key:value'"; 75 | errors['706'] = "'path' parameter is mandatory"; 76 | 77 | // Headers 78 | errors['800'] = "Error adding agent due to header 'x-forwarded-for' is not present"; 79 | errors['801'] = "Wrong format for 'wazuh-app-version' header. Expected format: 'X.Y.Z'"; 80 | errors['802'] = "Invalid 'wazuh-app-version' header"; 81 | errors['803'] = "Wazuh API is only available for master nodes"; 82 | errors['804'] = "Invalid content-type for this request. Content-type should be 'application/xml' or 'application/octet-stream'"; 83 | 84 | exports.description = function(n){ 85 | if (n in errors) 86 | return errors[n]; 87 | else 88 | return "Undefined error."; 89 | } 90 | -------------------------------------------------------------------------------- /helpers/execute.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | var logger = require('../helpers/logger'); 13 | var errors = require('../helpers/errors'); 14 | var timeout = 240; // seconds 15 | var disable_timeout = false; 16 | 17 | /** 18 | * Exec command. 19 | * It returns (callback) always a JSON. 20 | * Input 21 | * Error: {'error': !=0, 'message': 'Error description'} 22 | * OK: {'error': 0, 'data' = 'cmd output'} 23 | * Output 24 | * Error: {'error': !=0, 'message': 'Error description'} 25 | * OK: {'error': 0, 'data' = 'cmd output'} 26 | */ 27 | exports.exec = function(cmd, args, stdin, callback) { 28 | const child_process = require('child_process'); 29 | 30 | if (!(stdin instanceof Object)) { 31 | err = "Error executing command: stdin value must be an object: " + stdin 32 | logger.error("CMD - " + err); 33 | error = true; 34 | callback({"error": 1, "message": err}); 35 | return; 36 | } 37 | 38 | // log 39 | stdin['arguments']['wait_for_complete'] = disable_timeout; 40 | var full_cmd = "CMD - Command: " + cmd + " args:" + args.join(' ') + " stdin:" + JSON.stringify(stdin); 41 | logger.debug(full_cmd); 42 | 43 | const child = child_process.spawn(cmd, args); 44 | 45 | var output = []; 46 | var error = false; 47 | var close = false; 48 | var tout = false; 49 | 50 | if (!disable_timeout) { 51 | setTimeout(function(){ 52 | logger.debug("Sending SIGTERM to " + full_cmd); 53 | child.kill('SIGTERM'); 54 | tout = true; 55 | }, timeout*1000); 56 | } else { 57 | logger.log("Timeout has been disabled in this API call."); 58 | } 59 | 60 | // Delay to prevent write stdin when the pipe is closed. 61 | setTimeout(function(){ 62 | if (!close){ 63 | child.stdin.setEncoding('utf-8'); 64 | child.stdin.write(JSON.stringify(stdin) +"\n"); 65 | } 66 | }, 50); 67 | 68 | child.stdout.on('data', (chunk) => { 69 | output.push(chunk) 70 | //logger.debug("Chunk: " + Buffer.byteLength(chunk, 'utf8') + " bytes"); 71 | }); 72 | 73 | child.on('error', function(err) { 74 | // Reset disable timeout 75 | disable_timeout = false; 76 | logger.error("CMD - Error executing command: " + err); 77 | error = true; 78 | callback({"error": 1, "message": errors.description(1)}); // Error executing internal command 79 | }); 80 | 81 | child.on('close', (code) => { 82 | // Reset disable timeout 83 | disable_timeout = false; 84 | logger.debug("CMD - Exit code: " + code); 85 | close = true; 86 | if (!error){ 87 | var json_result = {}; 88 | 89 | if (code != 0){ // Exit code must be 0 90 | if (tout) 91 | json_result = {"error": 1, "message": errors.description(1) + ". Timeout exceeded (" + timeout + "s)."}; // Error executing internal command 92 | else 93 | json_result = {"error": 1, "message": errors.description(1) + ". Exit code: " + code}; // Error executing internal command 94 | } 95 | else{ 96 | var json_cmd = {} 97 | // Check JSON 98 | var stdout = output.join(''); 99 | logger.debug("CMD - STDOUT:\n---\n" + stdout + "\n---"); 100 | logger.debug("CMD - STDOUT: " + Buffer.byteLength(stdout, 'utf8') + " bytes"); 101 | json_cmd = tryParseJSON(stdout) 102 | 103 | if (!json_cmd){ 104 | logger.debug("CMD - STDOUT NOT JSON"); 105 | json_result = {"error": 2, "message": errors.description(2)}; // OUTPUT Not JSON 106 | } 107 | else{ 108 | // Check JSON content 109 | if ( json_cmd.hasOwnProperty('error') && ( json_cmd.hasOwnProperty('message') || json_cmd.hasOwnProperty('data') ) ){ 110 | 111 | json_result.error = json_cmd.error; 112 | 113 | if ( json_cmd.hasOwnProperty('data') ) 114 | json_result.data = json_cmd.data; 115 | 116 | if ( json_cmd.hasOwnProperty('message') ){ 117 | logger.error(json_cmd.message); 118 | if ( json_result.error === 1000) 119 | json_result.message = "Internal error"; 120 | else{ 121 | if (typeof json_cmd.message === 'string') 122 | json_result.message = json_cmd.message.split(":", 1)[0]; 123 | } 124 | 125 | } 126 | } 127 | else{ 128 | json_result = {"error": 1, "message": errors.description(1) + ". Wrong keys"}; // JSON Wrong keys 129 | logger.error("CMD - Wrong keys: " + Object.keys(json_cmd)); 130 | } 131 | } 132 | } 133 | callback(json_result); 134 | } 135 | }); 136 | 137 | } 138 | 139 | exports.set_disable_timeout = function(new_value) { 140 | disable_timeout = new_value; 141 | } 142 | 143 | function tryParseJSON (jsonString){ 144 | try { 145 | var o = JSON.parse(jsonString); 146 | 147 | if (o && typeof o === "object" && o !== null) { 148 | return o; 149 | } 150 | } 151 | catch (e) { } 152 | 153 | return false; 154 | }; 155 | -------------------------------------------------------------------------------- /helpers/files.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | var fs = require('fs'); 13 | var moment = require('moment'); 14 | 15 | /* 16 | Creates a temporary file with a random name containing file_contents. 17 | Returns the file name 18 | */ 19 | exports.tmp_file_creator = function(file_contents) { 20 | 21 | random_file_name = 'tmp/api_group_conf_' + moment().unix() + '_' + Math.floor(Math.random() * Math.floor(1000)).toString(); 22 | fs.writeFileSync(config.ossec_path + '/' + random_file_name, file_contents); 23 | 24 | return random_file_name; 25 | } 26 | -------------------------------------------------------------------------------- /helpers/filters.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | var res_h = require('../helpers/response_handler'); 13 | var validator = require('../helpers/input_validation'); 14 | var errors = require('../helpers/errors'); 15 | 16 | /* 17 | * query = req.query, req.params 18 | * filters = {'filter1':'type', 'filter2':'type'} 19 | * res = response 20 | * 21 | * Return: 22 | * True, if matched filter 23 | * False: Error in filter. Also, it sends a response!* 24 | */ 25 | exports.check = function (query, filters, req, res){ 26 | var query_aux = (JSON.parse(JSON.stringify(query))); // Clone query 27 | 28 | for(var field in filters){ 29 | if (field in query){ 30 | var type = filters[field] 31 | if(!validator[type](query[field])){ // Bad type 32 | var erro_code = errors.description(type); 33 | res_h.bad_request(req, res, erro_code, " Field: " + field); 34 | return false; 35 | } 36 | delete query_aux[field]; 37 | } 38 | } 39 | 40 | // No extra fields 41 | if (Object.keys(query_aux).length != 0){ 42 | res_h.bad_request(req, res, 604, filters_to_string(filters)); 43 | return false; //Error: Allowed fields 44 | } 45 | 46 | return true; // Filter OK 47 | } 48 | 49 | exports.check_xml = function(xml_string, req, res) { 50 | var parser = require('fast-xml-parser'); 51 | var is_valid = parser.validate(xml_string); 52 | if (is_valid === true) { 53 | return true; 54 | } else { 55 | res_h.bad_request(req, res, 703, is_valid.err.msg); 56 | return false; 57 | }; 58 | } 59 | 60 | exports.check_path = function(path, req, res, local_paths) { 61 | if (path.includes('./') || path.includes('../')) { 62 | res_h.bad_request(req, res, 704); 63 | return false 64 | } 65 | 66 | // allow global rules and decoders for GET requests 67 | if (local_paths) { 68 | var re_local = new RegExp(/^((etc\/ossec.conf)|(etc\/rules\/|etc\/decoders\/)[\w\-\/]+\.{1}xml|(etc\/lists\/)[\w\-\.\/]+)$/) 69 | if (!re_local.test(path)) { 70 | res_h.bad_request(req, res, 704); 71 | return false 72 | } 73 | } else { 74 | var re_global = new RegExp(/^((etc\/ossec.conf)|(etc\/rules\/|etc\/decoders\/|ruleset\/rules\/|ruleset\/decoders\/)[\w\-\/]+\.{1}xml|(etc\/lists\/)[\w\-\.\/]+)$/) 75 | if (!re_global.test(path)) { 76 | res_h.bad_request(req, res, 704); 77 | return false 78 | } 79 | } 80 | 81 | return true 82 | } 83 | 84 | exports.check_cdb_list = function(cdb_list, req, res) { 85 | // for each line 86 | re = new RegExp(/^[^:]+:[^:]*$/) 87 | var cdb_list_splitted = cdb_list.split(/\r?\n/) 88 | 89 | for (i=0; i!\-.+\s:\/()'"|=]+$/); 64 | } 65 | 66 | exports.sort_param = function(param) { 67 | return input_val(param, /^[a-zA-Z0-9_\-\,\s\+\.]+$/); // + is translated as \s 68 | } 69 | 70 | exports.search_param = function(param) { 71 | return input_val(param, /^[^;\|&\^*>]+$/); 72 | } 73 | 74 | exports.select_param = function(param) { 75 | return input_val(param, /^[a-zA-Z0-9_\,\.]+$/); 76 | } 77 | 78 | exports.ranges = function(range) { 79 | return input_val(range, /^[0-9]+$|^[0-9]{1,2}\-[0-9]{1,2}$/); 80 | } 81 | 82 | exports.hashes = function(hash) { 83 | return input_val(hash, /^[0-9a-fA-F]{32}(?:[0-9a-fA-F]{8})?$|(?:[0-9a-fA-F]{32})?$/); // md5, sha1 or sha256 84 | } 85 | 86 | exports.ossec_key = function(key) { 87 | return input_val(key, /^[a-zA-Z0-9]+$/); 88 | } 89 | 90 | // [n_days]d[n_hours]h[n_minutes]m[n_seconds]s 91 | exports.timeframe_type = function(timeframe) { 92 | return input_val(timeframe, /^(\d{1,}[d|h|m|s]?){1}$/); 93 | } 94 | 95 | exports.empty_boolean = function(b) { 96 | return input_val(b, /^$|(^true|false$)/); 97 | } 98 | 99 | exports.yes_no_boolean = function(b) { 100 | return input_val(b, /^yes$|^no$/); 101 | } 102 | 103 | exports.boolean = function(b) { 104 | return input_val(b, /^true|false$/); 105 | } 106 | 107 | exports.query_param = function(q) { 108 | return input_val(q, /^(?:\(*[\w\.\-]+(?:=|!=|<|>|~)[\[\]\{\}\\\w\.\-\:\%\/\s]+\)*)(?:(?:;|,)\(*[\w\.\-]+(?:=|!=|<|>|~)[\[\]\{\}\\\w\.\-\:\%\/\s]+\)*)*$/); 109 | } 110 | 111 | exports.format = function(q) { 112 | return input_val(q, /^xml|json$/) 113 | } 114 | 115 | exports.encoded_uri = function(e) { 116 | return input_val(e, /^[a-zA-Z0-9_,\-\.\+\s\:@<>\/]+$/) 117 | } 118 | -------------------------------------------------------------------------------- /helpers/logger.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | var moment = require('moment'); 13 | var fs = require('fs'); 14 | var path = require('path'); 15 | var rfs = require('rotating-file-stream'); 16 | 17 | var tag = "WazuhAPI"; 18 | var absolute_path_log = config.log_path; 19 | var file_log = path.basename(config.log_path); 20 | var path_log = path.dirname(config.log_path); 21 | var foreground = false; 22 | var ossec_uid = 0; 23 | var ossec_gid = 0; 24 | 25 | // get ossec uid and gid 26 | 27 | var uidNumber = require("uid-number"); 28 | uidNumber("ossec", "ossec", function(er, uid, gid) { 29 | ossec_uid = gid; 30 | ossec_gid = uid; 31 | }); 32 | 33 | var LEVEL_DISABLED = 0; 34 | var LEVEL_INFO = 1; 35 | var LEVEL_WARNING = 2; 36 | var LEVEL_ERROR = 3; 37 | var LEVEL_DEBUG = 4; 38 | var user = ""; 39 | 40 | var logger_level = LEVEL_INFO; 41 | switch (config.logs.toLowerCase()) { 42 | case "info": 43 | logger_level = LEVEL_INFO; 44 | break; 45 | case "warning": 46 | logger_level = LEVEL_WARNING; 47 | break; 48 | case "error": 49 | logger_level = LEVEL_ERROR; 50 | break; 51 | case "debug": 52 | logger_level = LEVEL_DEBUG; 53 | break; 54 | case "disabled": 55 | logger_level = LEVEL_DISABLED; 56 | break; 57 | default: 58 | logger_level = LEVEL_INFO; 59 | } 60 | 61 | exports.set_user = function(req_user) { 62 | user = req_user; 63 | } 64 | 65 | exports.set_foreground = function() { 66 | foreground = true; 67 | } 68 | 69 | function header(){ 70 | return tag + " " + moment().format('YYYY-MM-DD HH:mm:ss') + " " + user + ": "; 71 | } 72 | 73 | function write_log(msg) { 74 | if (foreground) 75 | console.log(msg); 76 | fs.appendFile(absolute_path_log, msg + "\n", { 'mode': 0o640 }, function (err) { 77 | if (err) { 78 | return console.error(err); 79 | } 80 | }); 81 | } 82 | 83 | exports.log = function (message) { 84 | if (logger_level >= LEVEL_INFO) 85 | write_log(header() + message); 86 | } 87 | 88 | exports.warning = function (message) { 89 | if (logger_level >= LEVEL_WARNING) 90 | write_log(header() + message); 91 | } 92 | 93 | exports.error = function (message) { 94 | if (logger_level >= LEVEL_ERROR) 95 | write_log(header() + message); 96 | } 97 | 98 | exports.debug = function (message) { 99 | if (logger_level >= LEVEL_DEBUG) 100 | write_log(header() + message); 101 | } 102 | 103 | function pad(num) { 104 | return (num > 9 ? "" : "0") + num; 105 | } 106 | 107 | function generator(time, index) { 108 | if (!time) 109 | return path_log + "/" + file_log; 110 | 111 | var month = moment.monthsShort(time.getMonth()); 112 | var day = pad(time.getDate()); 113 | var year = pad(time.getFullYear()); 114 | 115 | return path_log + "/api/" + year + "/" + month + "/api-" + day + "-" + index + ".gz"; 116 | } 117 | 118 | var stream = rfs(generator, { 119 | interval: '1d', 120 | compress: true, 121 | rotationTime: true, 122 | mode: 0o640, 123 | }); 124 | 125 | stream.on('rotated', function(filename) { 126 | try { 127 | // rotation job completed with success producing given filename 128 | // setting correct permissions for generated files 129 | logger.log("Rotated: " + filename); 130 | fs.chmodSync(filename, 0o640); 131 | fs.chmodSync(path.dirname(filename), 0o750); 132 | fs.chmodSync(path.dirname(path.dirname(filename)), 0o750); 133 | 134 | // if the API is running as root, set the user of the created files to ossec 135 | if (!config.drop_privileges) { 136 | fs.chownSync(filename, ossec_uid, ossec_gid); 137 | fs.chownSync(path.dirname(filename), ossec_uid, ossec_gid); 138 | fs.chownSync(path.dirname(path.dirname(filename)), ossec_uid, ossec_gid); 139 | fs.chownSync(absolute_path_log, ossec_uid, ossec_gid); 140 | } 141 | 142 | // Prevents from crashing the service if the above instructions fail 143 | } catch (error) { 144 | try { 145 | logger.error(error.message || error); 146 | } catch (err) { 147 | console.log(err.message || err) 148 | } 149 | } 150 | }); 151 | -------------------------------------------------------------------------------- /helpers/request_templates.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2018 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | /* 13 | Function used in API requests that return a list with strings. 14 | Examples: 15 | GET/agents/os/summary 16 | GET/rootcheck/:agent_id/pci 17 | 18 | Field descritions: 19 | * entrypoint_name -> Name of the entrypoint (example: /agents, /agents/os/summary) 20 | * req -> parameter req 21 | * res -> parameter res 22 | * apiCacheGroup -> api cache group of the API call 23 | * param_checks -> Input validation checks for arguments in req.params. 24 | * query_checks -> Input validation checks for arguments in req.query. 25 | */ 26 | exports.single_field_array_request = function(entrypoint_name, req, res, apicacheGroup, param_checks, query_checks, single_object=false) { 27 | if(!param_checks || typeof param_checks !== 'object'){ 28 | param_checks = {}; 29 | } 30 | 31 | if(!query_checks || typeof query_checks !== 'object'){ 32 | query_checks = {}; 33 | } 34 | 35 | logger.debug(req.connection.remoteAddress + " GET " + entrypoint_name); 36 | 37 | req.apicacheGroup = apicacheGroup; 38 | 39 | var data_request = {'function': entrypoint_name, 'arguments': {}}; 40 | if (single_object) 41 | var filters = {} 42 | else 43 | var filters = {'offset': 'numbers', 'limit': 'numbers', 'sort':'sort_param', 44 | 'search':'search_param', 'distinct': 'empty_boolean', 'q':'query_param'}; 45 | 46 | if (!filter.check(req.query, Object.assign({}, filters, query_checks), req, res)) // Filter with error 47 | return; 48 | 49 | if (!filter.check(req.params, param_checks, req, res)) // Filter with error 50 | return; 51 | 52 | if ('offset' in req.query) 53 | data_request['arguments']['offset'] = Number(req.query.offset); 54 | if ('limit' in req.query) 55 | data_request['arguments']['limit'] = Number(req.query.limit); 56 | if ('sort' in req.query) 57 | data_request['arguments']['sort'] = filter.sort_param_to_json(req.query.sort); 58 | if ('search' in req.query) 59 | data_request['arguments']['search'] = filter.search_param_to_json(req.query.search); 60 | if ('distinct' in req.query) 61 | data_request['arguments']['distinct'] = req.query.distinct !== 'false'; 62 | if ('q' in req.query) 63 | data_request['arguments']['q'] = req.query.q; 64 | 65 | filters = {} 66 | 67 | for (extra in Object.assign({}, query_checks, param_checks)) { 68 | if (extra in req.query) { 69 | if (query_checks[extra] == 'select_param') 70 | data_request['arguments'][extra] = filter.select_param_to_json(req.query[extra]); 71 | else if (extra == 'summary') 72 | data_request['arguments'][extra] = req.query[extra] === 'yes' 73 | else if (!(extra in data_request['arguments'])) 74 | filters[extra] = req.query[extra]; 75 | } else if (extra in req.params) 76 | data_request['arguments'][extra] = req.params[extra]; 77 | } 78 | 79 | if (Object.keys(filters).length > 0) data_request['arguments']['filters'] = filters 80 | 81 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { res_h.send(req, res, data); }); 82 | } 83 | 84 | /* 85 | Function used in API requests that return a list with dictionaries. Its the same as "single_field_array_request" but includes field "select". 86 | Examples: 87 | GET/agents 88 | GET/rootcheck/:agent_id 89 | */ 90 | exports.array_request = function (entrypoint_name, req, res, apicacheGroup, param_checks, query_checks, single_object=false) { 91 | if(!param_checks || typeof param_checks !== 'object'){ 92 | param_checks = {}; 93 | } 94 | 95 | if(!query_checks || typeof query_checks !== 'object'){ 96 | query_checks = {}; 97 | } 98 | query_checks['select'] = 'select_param'; 99 | this.single_field_array_request(entrypoint_name, req, res, apicacheGroup, param_checks, query_checks, single_object=single_object); 100 | } 101 | 102 | /* 103 | Function used in API requests that return a single object. 104 | */ 105 | exports.object_request = function(entrypoint_name, req, res, apiCacheGroup, param_checks, query_checks) { 106 | this.array_request(entrypoint_name, req, res, apiCacheGroup, param_checks, query_checks, true); 107 | } 108 | -------------------------------------------------------------------------------- /helpers/response_handler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Wazuh RESTful API 3 | * Copyright (C) 2015-2020 Wazuh, Inc. All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | var errors = require('../helpers/errors'); 13 | var logger = require('../helpers/logger'); 14 | var conf = require('../configuration/config'); 15 | var fileSystem = require('fs'); 16 | 17 | exports.send = function(req, res, json_r, status){ 18 | 19 | if (typeof status == 'undefined') 20 | status = 200; 21 | 22 | // Validate json and status 23 | if (json_r != null && json_r.error != null && (json_r.data != null || json_r.message != null) && status >= 100 && status <= 600){ 24 | 25 | // Calculate status 26 | if (json_r.error >= 1 && json_r.error <= 9) 27 | status = 500; 28 | 29 | } 30 | else{ 31 | json_r = {"error": 3, "message": errors.description(3)}; // Internal Error 32 | status = 500; 33 | } 34 | 35 | // Logging 36 | var log_msg = "[" + req.connection.remoteAddress + "] " + req.method + " " + req.baseUrl + req.url + " - " + status + " - error: '" + json_r.error + "'."; 37 | logger.log(log_msg); 38 | 39 | if (status != 200) 40 | logger.debug("Response: " + JSON.stringify(json_r) + " HTTP Status: " + status); 41 | 42 | // Send 43 | if (!res.headersSent){ 44 | if (req['pretty']) 45 | res.status(status).send(JSON.stringify( json_r, null, 3) + "\n"); 46 | else 47 | res.status(status).json(json_r); 48 | } 49 | } 50 | 51 | exports.bad_request = function(req, res, internal_error, extra_msg){ 52 | var msg = errors.description(internal_error); 53 | 54 | if (extra_msg) 55 | msg = msg + ". " + extra_msg; 56 | 57 | json_res = {'error': internal_error, 'message': msg}; 58 | 59 | this.send(req, res, json_res, 400); 60 | } 61 | 62 | exports.unauthorized_request = function(req, res, internal_error, extra_msg){ 63 | var msg = errors.description(internal_error); 64 | 65 | if (extra_msg) 66 | msg = msg + ". " + extra_msg; 67 | 68 | json_res = {'error': internal_error, 'message': msg}; 69 | 70 | this.send(req, res, json_res, 401); 71 | } 72 | 73 | exports.send_file = function(req, res, file_name, type){ 74 | 75 | if (type == 'zip') { 76 | var real_filename = conf.ossec_path + file_name.data; 77 | var send_aux = this.send; 78 | 79 | try { 80 | var stat = fileSystem.statSync(real_filename); 81 | 82 | res.writeHead(200, { 83 | 'Content-Type': 'text/xml', 84 | 'Content-Length': stat.size 85 | }); 86 | 87 | var readStream = fileSystem.createReadStream(real_filename); 88 | readStream 89 | .on('close', function(err) { 90 | // remove .zip file once it has been sent 91 | fileSystem.unlink(real_filename); 92 | }); 93 | 94 | readStream.pipe(res) 95 | 96 | // Logging 97 | var log_msg = "[" + req.connection.remoteAddress + "] " + req.method + " " + req.baseUrl + req.url + " - 200 - error: '0'."; 98 | logger.log(log_msg); 99 | } catch (e) { 100 | json_res = {'error': 3, 'message': errors.description(3)}; 101 | send_aux(req, res, json_res, 500); 102 | return; 103 | } 104 | 105 | }else{ 106 | 107 | var data_request = {'function': '/' + type +'/files', 'arguments': {'file': file_name}}; 108 | 109 | var send_aux = this.send; 110 | execute.exec(python_bin, [wazuh_control], data_request, function (data) { 111 | try { 112 | try { 113 | var filepath = data.data.items[0].path + "/" + file_name; 114 | } catch (e) { 115 | json_res = {'error': 700, 'message': errors.description(700) + ": " + file_name}; 116 | send_aux(req, res, json_res, 404); 117 | return; 118 | } 119 | var stat = fileSystem.statSync(conf.ossec_path + '/' + filepath); 120 | 121 | res.writeHead(200, { 122 | 'Content-Type': 'text/xml', 123 | 'Content-Length': stat.size 124 | }); 125 | 126 | var readStream = fileSystem.createReadStream(conf.ossec_path + '/' + filepath); 127 | 128 | readStream.pipe(res) 129 | 130 | // Logging 131 | var log_msg = "[" + req.connection.remoteAddress + "] " + req.method + " " + req.baseUrl + req.url + " - 200 - error: '0'."; 132 | logger.log(log_msg); 133 | } catch (e) { 134 | if (e.code === 'ENOENT') { 135 | json_res = {'error': 700, 'message': errors.description(700) + ": " + filepath}; 136 | send_aux(req, res, json_res, 404); 137 | return; 138 | } else { 139 | json_res = {'error': 3, 'message': errors.description(3)}; 140 | send_aux(req, res, json_res, 500); 141 | return; 142 | } 143 | } 144 | }); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /models/wazuh-api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Created by Wazuh, Inc. . 4 | # This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2 5 | 6 | from sys import argv, exit 7 | from getopt import getopt, GetoptError 8 | import json 9 | import signal 10 | import logging 11 | import time 12 | import asyncio 13 | 14 | error_wazuh_package = 0 15 | exception_error = None 16 | try: 17 | from wazuh import Wazuh 18 | from wazuh.exception import WazuhException 19 | from wazuh.cluster.dapi import dapi 20 | except (ImportError, SyntaxError) as e: 21 | error = str(e) 22 | error_wazuh_package = -1 23 | except WazuhException as e: 24 | error_wazuh_package = -3 25 | error = e.message 26 | error_code = e.code 27 | except Exception as e: 28 | error = str(e) 29 | if str(e).startswith("Error 4000"): 30 | error_wazuh_package=-1 31 | else: 32 | error_wazuh_package = -2 33 | exception_error = e 34 | 35 | 36 | def print_json(data, error=0): 37 | output = {'error': error} 38 | 39 | if error == 0: 40 | key = 'data' 41 | else: 42 | key = 'message' 43 | 44 | output[key] = data 45 | 46 | if pretty: 47 | print(json.dumps(output, indent=4)) 48 | else: 49 | print(json.dumps(output)) 50 | 51 | 52 | def is_json(myjson): 53 | try: 54 | json_object = json.loads(myjson) 55 | except: 56 | return False 57 | 58 | return json_object 59 | 60 | 61 | def get_stdin(msg): 62 | stdin = input(msg) 63 | return stdin 64 | 65 | 66 | def signal_handler(n_signal, frame): 67 | exit(1) 68 | 69 | 70 | def usage(): 71 | help_msg = ''' 72 | Wazuh Control 73 | 74 | \t-p, --pretty Pretty JSON 75 | \t-d, --debug Debug mode 76 | \t-l, --list List functions 77 | \t-h, --help Help 78 | ''' 79 | print(help_msg) 80 | exit(1) 81 | 82 | if __name__ == "__main__": 83 | logging.basicConfig(level=logging.DEBUG) 84 | 85 | request = {} 86 | pretty = False 87 | debug = False 88 | list_f = False 89 | 90 | # Read and check arguments 91 | try: 92 | opts, args = getopt(argv[1:], "pdlh", ["pretty", "debug", "list", "help"]) 93 | n_args = len(opts) 94 | if not (0 <= n_args <= 2): 95 | print("Incorrect number of arguments.\nTry '--help' for more information.") 96 | exit(1) 97 | except GetoptError as err_args: 98 | print(str(err_args)) 99 | print("Try '--help' for more information.") 100 | exit(1) 101 | 102 | for o, a in opts: 103 | if o in ("-p", "--pretty"): 104 | pretty = True 105 | elif o in ("-d", "--debug"): 106 | debug = True 107 | elif o in ("-l", "--list"): 108 | list_f = True 109 | elif o in ("-h", "--help"): 110 | usage() 111 | else: 112 | print("Wrong argument combination.") 113 | print("Try '--help' for more information.") 114 | exit(1) 115 | 116 | signal.signal(signal.SIGTERM, signal_handler) 117 | signal.signal(signal.SIGINT, signal_handler) 118 | 119 | if not list_f: 120 | stdin = get_stdin("") 121 | request = is_json(stdin) 122 | if not request: 123 | print_json("Wazuh-Python Internal Error: Bad JSON input", 1000) 124 | exit(1) 125 | 126 | if error_wazuh_package < 0: 127 | if error_wazuh_package == -1: 128 | print_json("Wazuh-Python Internal Error: {0}".format(error), 1000) 129 | if error_wazuh_package == -2: 130 | print_json("Wazuh-Python Internal Error: uncaught exception: {0}".format(exception_error), 1000) 131 | if error_wazuh_package == -3: 132 | print_json(error, error_code) 133 | exit(0) # error code 0 shows the msg in the API response. 134 | 135 | if 'function' not in request: 136 | print_json("Wazuh-Python Internal Error: 'JSON input' must have the 'function' key", 1000) 137 | exit(1) 138 | 139 | # Main 140 | try: 141 | before = time.time() 142 | 143 | if list_f: 144 | print_json(sorted(dapi.get_functions())) 145 | exit(0) 146 | 147 | request['from_cluster'] = False 148 | data = asyncio.run(dapi.DistributedAPI(input_json=request, logger=logging.getLogger(), 149 | debug=debug, pretty=pretty).distribute_function()) 150 | after = time.time() 151 | logging.debug("Total time: {}".format(after - before)) 152 | logging.debug("Size of all received data: {}".format(len(data))) 153 | 154 | print(data) 155 | 156 | except WazuhException as e: 157 | print_json(e.message, e.code) 158 | if debug: 159 | raise 160 | except Exception as e: 161 | print_json("Wazuh-Python Internal Error: {0}".format(str(e)), 1000) 162 | if debug: 163 | raise 164 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wazuh_api", 3 | "version": "3.13.6", 4 | "revision": "31306", 5 | "description": "Wazuh API.", 6 | "main": "app.js", 7 | "author": "Wazuh", 8 | "license": "GPL-2.0", 9 | "dependencies": { 10 | "apicache": "~0.2.0", 11 | "body-parser": "~1.19.0", 12 | "cors": "~2.8.0", 13 | "express": "~4.17.0", 14 | "fast-xml-parser": "~3.12.11", 15 | "htpasswd": "~2.4.0", 16 | "http-auth": "~3.2.4", 17 | "moment": "~2.29.3", 18 | "rotating-file-stream": "~1.4.6", 19 | "uid-number": "~0.0.6" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/wazuh/wazuh-api.git" 24 | }, 25 | "bugs": { 26 | "url": "https://github.com/wazuh/wazuh-api/issues" 27 | }, 28 | "devDependencies": { 29 | "glob": "^7.1.3", 30 | "mocha": "^6.2.0", 31 | "mochawesome": "^3.1.1", 32 | "should": "^11.2.1", 33 | "supertest": "^4.0.2" 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /scripts/bump_version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Bump source version 4 | # Wazuh Inc. 5 | 6 | # Syntax: 7 | # bump_version [ ] [ ] 8 | # Example: 9 | # ./bump_version.sh v3.0.0-alpha1 -r 1000 10 | 11 | version=$1 12 | revision=$2 13 | 14 | if [ -z "$version" ] && [ -z "$revision" ] 15 | then 16 | echo "Error: no arguments given." 17 | echo "Syntax: $0 [ ] [ ]" 18 | exit 1 19 | fi 20 | 21 | cd $(dirname $0) 22 | 23 | PACKAGE_FILE="../package.json" 24 | # APP_FILE="../app.js" 25 | 26 | if [ -n "$version" ] 27 | then 28 | grep "\"version\":" $PACKAGE_FILE > /dev/null 29 | 30 | if [ $? != 0 ] 31 | then 32 | echo "Error: no suitable version definition found at file $PACKAGE_FILE" 33 | exit 1 34 | fi 35 | 36 | sed -E -i'' "s/\"version\": \".+\",/\"version\": \"$version\",/g" $PACKAGE_FILE 37 | 38 | 39 | # grep "current_version =" $APP_FILE > /dev/null 40 | 41 | # if [ $? != 0 ] 42 | # then 43 | # echo "Error: no suitable version definition found at file $APP_FILE" 44 | # exit 1 45 | # fi 46 | 47 | # sed -E -i'' "s/current_version = \".+\";/current_version = \"v$version\";/g" $APP_FILE 48 | 49 | fi 50 | 51 | if [ -n "$revision" ] 52 | then 53 | grep "\"revision\":" $PACKAGE_FILE > /dev/null 54 | 55 | if [ $? != 0 ] 56 | then 57 | echo "Error: no suitable revision definition found at file $PACKAGE_FILE" 58 | exit 1 59 | fi 60 | 61 | sed -E -i'' "s/\"revision\": \".+\",/\"revision\": \"$revision\",/g" $PACKAGE_FILE 62 | fi 63 | -------------------------------------------------------------------------------- /scripts/install_daemon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (C) 2015-2018 Wazuh, Inc. All rights reserved. 4 | # Wazuh.com 5 | # This program is a free software; you can redistribute it 6 | # and/or modify it under the terms of the GNU General Public 7 | # License (version 2) as published by the FSF - Free Software 8 | # Foundation. 9 | 10 | # Installer for Wazuh API daemon 11 | # Wazuh Inc. 12 | 13 | 14 | I_OWNER="root" 15 | I_GROUP="root" 16 | I_XMODE="755" 17 | I_FMODE="644" 18 | I_SYSTEMD="/etc/systemd/system" 19 | I_SYSVINIT="/etc/init.d" 20 | 21 | OSSEC_CONF="/etc/ossec-init.conf" 22 | DEF_OSSDIR="/var/ossec" 23 | 24 | # Test root permissions 25 | 26 | if [ "$EUID" -ne 0 ]; then 27 | echo "Warning: Please run this script with root permissions." 28 | fi 29 | 30 | # Directory where OSSEC is installed 31 | 32 | if ! [ -f $OSSEC_CONF ]; then 33 | echo "Can't find $OSSEC_CONF. Is OSSEC installed?" 34 | exit 1 35 | fi 36 | 37 | . $OSSEC_CONF 38 | 39 | if [ -z "$DIRECTORY" ]; then 40 | DIRECTORY=$DEF_OSSDIR 41 | fi 42 | 43 | APP_PATH="${DIRECTORY}/api/app.js" 44 | SCRIPTS_PATH="${DIRECTORY}/api/scripts" 45 | 46 | if ! [ -f $APP_PATH ]; then 47 | echo "Can't find $APP_PATH. Is Wazuh API installed?" 48 | exit 1 49 | fi 50 | 51 | # Binary name for NodeJS 52 | 53 | BIN_DIR=$(command -v nodejs 2> /dev/null) 54 | 55 | if [ "X$BIN_DIR" = "X" ]; then 56 | BIN_DIR=$(command -v node 2> /dev/null) 57 | 58 | if [ "X$BIN_DIR" = "X" ]; then 59 | echo "NodeJS binaries not found. Is NodeJS installed?" 60 | exit 1 61 | fi 62 | fi 63 | 64 | # Install for systemd 65 | 66 | if command -v systemctl > /dev/null 2>&1 && systemctl > /dev/null 2>&1; then 67 | echo "Installing for systemd" 68 | 69 | sed "s:^ExecStart=.*:ExecStart=$BIN_DIR $APP_PATH:g" $SCRIPTS_PATH/wazuh-api.service > $SCRIPTS_PATH/wazuh-api.service.tmp 70 | install -m $I_FMODE -o $I_OWNER -g $I_GROUP $SCRIPTS_PATH/wazuh-api.service.tmp $I_SYSTEMD/wazuh-api.service 71 | rm $SCRIPTS_PATH/wazuh-api.service.tmp 72 | systemctl daemon-reload 73 | systemctl enable wazuh-api 74 | systemctl restart wazuh-api 75 | 76 | 77 | # Install for SysVinit / Upstart 78 | 79 | elif command -v service > /dev/null 2>&1; then 80 | echo "Installing for SysVinit" 81 | 82 | sed "s:^BIN_DIR=.*:BIN_DIR=\"$BIN_DIR\":g" $SCRIPTS_PATH/wazuh-api > $SCRIPTS_PATH/wazuh-api.tmp 83 | sed -i "s:^APP_PATH=.*:APP_PATH=\"$APP_PATH\":g" $SCRIPTS_PATH/wazuh-api.tmp 84 | sed -i "s:^OSSEC_PATH=.*:OSSEC_PATH=\"${DIRECTORY}\":g" $SCRIPTS_PATH/wazuh-api.tmp 85 | install -m $I_XMODE -o $I_OWNER -g $I_GROUP $SCRIPTS_PATH/wazuh-api.tmp $I_SYSVINIT/wazuh-api 86 | rm $SCRIPTS_PATH/wazuh-api.tmp 87 | 88 | enabled=true 89 | if command -v chkconfig > /dev/null 2>&1; then 90 | /sbin/chkconfig --add wazuh-api > /dev/null 2>&1 91 | elif [ -f "/usr/sbin/update-rc.d" ] || [ -n "$(ps -e | egrep upstart)" ]; then 92 | update-rc.d wazuh-api defaults 93 | elif [ -r "/etc/gentoo-release" ]; then 94 | rc-update add wazuh-api default 95 | else 96 | echo "init script installed in $I_SYSVINIT/wazuh-api" 97 | echo "We could not enable it. Please enable the service manually." 98 | enabled=false 99 | fi 100 | 101 | if [ "$enabled" = true ]; then 102 | service wazuh-api restart 103 | fi 104 | else 105 | echo "Warning: Unknown init system. Please run the API with:" 106 | echo "$BIN_DIR $APP_PATH > /dev/null 2>&1 < /dev/null &" 107 | fi 108 | -------------------------------------------------------------------------------- /scripts/wazuh-api: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # WAZUH API Service 3 | # Copyright (C) 2015-2018 Wazuh, Inc. All rights reserved. 4 | # Wazuh.com 5 | # This program is a free software; you can redistribute it 6 | # and/or modify it under the terms of the GNU General Public 7 | # License (version 2) as published by the FSF - Free Software 8 | # Foundation. 9 | 10 | ### BEGIN INIT INFO 11 | # Provides: wazuh_api 12 | # Required-Start: $remote_fs $syslog 13 | # Required-Stop: $remote_fs $syslog 14 | # Should-Start: $network 15 | # Should-Stop: $network 16 | # Default-Start: 2 3 4 5 17 | # Default-Stop: 0 1 6 18 | # Short-Description: Wazuh API 19 | # Description: Wazuh API daemon 20 | # 21 | ### END INIT INFO 22 | 23 | # Instructions: 24 | # sudo install -m 755 -o root -g root /var/ossec/api/scripts/wazuh-api /etc/init.d/ 25 | # sudo systemctl enable wazuh-api 26 | # sudo systemctl daemon-reload 27 | # sudo systemctl restart wazuh-api 28 | 29 | BIN_DIR= 30 | APP_PATH= 31 | OSSEC_PATH= 32 | PID_PATH="$OSSEC_PATH/var/run/wazuh-api.pid" 33 | API_LOGS="$OSSEC_PATH/logs/api.log" 34 | 35 | start() { 36 | if [ -f $PID_PATH ] && [ -n "$(ps --no-headers -p `cat $PID_PATH`)" ]; then 37 | echo "WAZUH-API is already running." 38 | else 39 | $BIN_DIR $APP_PATH > /dev/null 2>&1 < /dev/null & 40 | 41 | if [ "$?" = "0" ]; then 42 | echo $! > $PID_PATH 43 | else 44 | echo "Start error" 45 | exit 1 46 | fi 47 | 48 | echo "WAZUH-API started." 49 | fi 50 | } 51 | 52 | stop() { 53 | if [ -f $PID_PATH ] && [ -n "$(ps --no-headers -p `cat $PID_PATH`)" ]; then 54 | kill `cat $PID_PATH` > /dev/null 2>&1 55 | while [ -n "$(ps --no-headers -p `cat $PID_PATH`)" ]; do sleep 0.1; done 56 | rm -f $PID_PATH 57 | echo "WAZUH-API stopped." 58 | else 59 | echo "WAZUH-API is not running." 60 | fi 61 | } 62 | 63 | status() { 64 | if [ -f $PID_PATH ] && [ -n "$(ps --no-headers -p `cat $PID_PATH`)" ]; then 65 | echo "WAZUH-API is running." 66 | else 67 | echo "WAZUH-API is stopped." 68 | if [ -f $API_LOGS ]; then 69 | echo "" 70 | echo "Last 20 log entries:" 71 | tail -n 20 $API_LOGS 72 | echo "" 73 | fi 74 | echo "Full log: $API_LOGS" 75 | fi 76 | } 77 | 78 | case "$1" in 79 | start) 80 | start 81 | ;; 82 | stop) 83 | stop 84 | ;; 85 | restart) 86 | stop 87 | start 88 | ;; 89 | status) 90 | status 91 | ;; 92 | *) 93 | echo "*** Usage: $0 {start|stop|restart|status}" 94 | exit 1 95 | esac 96 | -------------------------------------------------------------------------------- /scripts/wazuh-api.service: -------------------------------------------------------------------------------- 1 | # WAZUH API Service (Systemd unit) 2 | # 3 | # Copyright (C) 2015-2018 Wazuh, Inc. All rights reserved. 4 | # Wazuh.com 5 | # This program is a free software; you can redistribute it 6 | # and/or modify it under the terms of the GNU General Public 7 | # License (version 2) as published by the FSF - Free Software 8 | # Foundation. 9 | 10 | [Unit] 11 | Description=Wazuh API daemon 12 | Documentation=https://documentation.wazuh.com/current/user-manual/api/index.html 13 | After=network-online.target 14 | 15 | [Service] 16 | Type=simple 17 | ExecStart= 18 | Environment=NODE_ENV=production 19 | ExecStop=/bin/kill -HUP $MAINPID 20 | KillMode=process 21 | 22 | [Install] 23 | WantedBy=multi-user.target 24 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Mocha tests 2 | 3 | ## Requirements 4 | 5 | * API installed and configurated (Configure API: https and auth (foo:bar)). 6 | * Packages npm installed: `glob`, `supertest`, `mocha`, `should`, `moment` 7 | 8 | ``` 9 | npm install mocha -g 10 | npm install glob supertest mocha should moment 11 | ``` 12 | 13 | * Cluster configurated and running with 2 connected nodes: `master` and `worker`. 14 | * Three connected agents (excluding manager): 15 | * id `000` and version _Wazuh v3.8.0_. Must have the following additional configuration: 16 | * Enable experimental features in `/var/ossec/api/configuration/config.js`: 17 | ``` 18 | config.experimental_features = true; 19 | ``` 20 | * Disable cache in the same config.js 21 | ``` 22 | config.cache_enabled = "no"; 23 | ``` 24 | * Agentless: 25 | ```shellsession 26 | # /var/ossec/bin/ossec-control enable agentless 27 | ``` 28 | ```xml 29 | 30 | ssh_integrity_check_linux 31 | 300 32 | admin@192.168.1.108 33 | periodic_diff 34 | /etc /usr/bin /usr/sbin 35 | 36 | ``` 37 | * Active response: 38 | ```xml 39 | 40 | no 41 | host-deny 42 | defined-agent 43 | 032 44 | 10 45 | sshd,|pci_dss_11.4, 46 | 1 47 | 48 | ``` 49 | * Client syslog 50 | ```shellsession 51 | # /var/ossec/bin/ossec-control enable client-syslog 52 | ``` 53 | ```xml 54 | 55 | 9 56 | 192.168.1.241 57 | 58 | ``` 59 | * Integration 60 | ```shellsession 61 | # /var/ossec/bin/ossec-control enable integrator 62 | ``` 63 | ```xml 64 | 65 | virustotal 66 | API_KEY 67 | syscheck 68 | json 69 | 70 | 71 | ``` 72 | * Logcollector socket: 73 | ```xml 74 | 75 | custom_socket 76 | /var/run/custom.sock 77 | tcp 78 | custom_syslog: 79 | 80 | ``` 81 | * Mail: 82 | 1. Follow steps detailed [here](https://documentation.wazuh.com/current/user-manual/manager/manual-email-report/smtp_authentication.html). 83 | 2. 84 | ```xml 85 | 86 | yes 87 | hello@wazuh.com 88 | localhost 89 | wazuh@test.com 90 | 91 | 92 | you@example.com 93 | 4 94 | 95 | 96 | ``` 97 | 98 | 99 | 100 | * id `001` and version _Wazuh v3.8.0_. 101 | * id `002` and version _Wazuh v3.8.0_. Must have the following additional configuration: 102 | * Labels: 103 | ```xml 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | ``` 112 | * id `003` and version _Wazuh v3.5.0-1_. 113 | 114 | * DB syscheck activated: Add `wazuh_database.sync_syscheck=1` to the file `/var/ossec/etc/local_internal_options.conf`. 115 | * Restart wazuh-manager. 116 | * Then start needed services: 117 | 1. Run `maild` service: 118 | ```shellsession 119 | # /var/ossec/bin/ossec-maild 120 | ``` 121 | 2. Run `authd` service: 122 | ```shellsession 123 | # /var/ossec/bin/ossec-authd 124 | ``` 125 | 126 | ## Prepare environment 127 | Every step detailed above can be automated by executing the following command from `./environment/vagrant` folder: 128 | ```shellsession 129 | # export WAZUH_API_PATH=/your/wazuh/api/path 130 | # vagrant up 131 | ``` 132 | 133 | ## Run all tests 134 | ```shellsession 135 | # cd /home/vagrant/wazuh_api 136 | # mocha ./test --timeout 10000 137 | ``` 138 | -------------------------------------------------------------------------------- /test/common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * API RESTful for OSSEC 3 | * Copyright (C) 2015-2016 Wazuh, Inc.All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | 13 | var config = {}; 14 | 15 | config.ossec_path = "/var/ossec" 16 | config.url = 'https://127.0.0.1:55000'; 17 | config.credentials = {'user':'foo', 'password':'bar'}; 18 | 19 | config.timeout = 30000; 20 | 21 | module.exports = config; 22 | -------------------------------------------------------------------------------- /test/data/agent.conf.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | no 5 | 3600 6 | 90d 7 | yes 8 | 9 | /opt/java/bin 10 | /opt/scanner 11 | 12 | 13 | level2 14 | 15 | 16 | 17 | 18 | full_command 19 | ciscat-java-check 20 | if [ $(find /opt/ossec/wodles/ciscat/content/java/bin/ -executable -type f 2>/dev/null | wc -l) -eq 1 ]; then echo "Check: Passed"; else echo "Check: Failed"; fi 21 | 86400 22 | 23 | 24 | 25 | full_command 26 | ciscat-scanner-check 27 | if [ $(ls /opt/ossec/wodles/ciscat/content/scanner/ 2>/dev/null | wc -l) -eq 4 ]; then echo "Check: Passed"; else echo "Check: Failed"; fi 28 | 86400 29 | 30 | 31 | 32 | full_command 33 | ciscat-ossec-log 34 | grep 'wazuh-modulesd:ciscat:' /opt/ossec/logs/ossec.log | tail -20 35 | 86400 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | /opt/logging 49 | 50 | /opt/c2online 51 | 52 | /opt/spring/c2online 53 | /opt/spring/addons 54 | /opt/spring/commons 55 | 56 | 57 | 58 | /opt/deploy/c2online*.war 59 | 60 | 61 | .log$ 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /test/data/invalid.conf.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | no 5 | 3600 6 | 90d 7 | yes 8 | 9 | /opt/java/bin 10 | /opt/scanner 11 | 12 | 13 | level2 14 | 15 | 16 | 17 | 18 | full_command 19 | ciscat-java-check 20 | if [ $(find /opt/ossec/wodles/ciscat/content/java/bin/ -executable -type f 2>/dev/null | wc -l) -eq 1 ]; then echo "Check: Passed"; else echo "Check: Failed"; fi 21 | 86400 22 | 23 | 24 | 25 | full_command 26 | ciscat-scanner-check 27 | if [ $(ls /opt/ossec/wodles/ciscat/content/scanner/ 2>/dev/null | wc -l) -eq 4 ]; then echo "Check: Passed"; else echo "Check: Failed"; fi 28 | 86400 29 | 30 | 31 | 32 | full_command 33 | ciscat-ossec-log 34 | grep 'wazuh-modulesd:ciscat:' /opt/ossec/logs/ossec.log | tail -20 35 | 86400 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | /opt/logging 49 | 50 | /opt/c2online 51 | 52 | /opt/spring/c2online 53 | /opt/spring/addons 54 | /opt/spring/commons 55 | 56 | 57 | 58 | /opt/deploy/c2online*.war 59 | 60 | 61 | .log$ 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /test/data/wrong.conf.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | no 5 | 3600 6 | 90d 7 | yes 8 | 9 | /opt/java/bin 10 | /opt/scanner 11 | 12 | 13 | level2 14 | 15 | 16 | 17 | 18 | full_command 19 | ciscat-java-check 20 | if [ $(find /opt/ossec/wodles/ciscat/content/java/bin/ -executable -type f 2>/dev/null | wc -l) -eq 1 ]; then echo "Check: Passed"; else echo "Check: Failed"; fi 21 | 86400 22 | 23 | 24 | 25 | full_command 26 | ciscat-scanner-check 27 | if [ $(ls /opt/ossec/wodles/ciscat/content/scanner/ 2>/dev/null | wc -l) -eq 4 ]; then echo "Check: Passed"; else echo "Check: Failed"; fi 28 | 86400 29 | 30 | 31 | 32 | full_command 33 | ciscat-ossec-log 34 | grep 'wazuh-modulesd:ciscat:' /opt/ossec/logs/ossec.log | tail -20 35 | 86400 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | /opt/logging 49 | 50 | /opt/c2online 51 | 52 | /opt/spring/c2online 53 | /opt/spring/addons 54 | /opt/spring/commons 55 | 56 | 57 | 58 | /opt/deploy/c2online*.war 59 | 60 | 61 | .log$ 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /test/environment/docker/centos/.env: -------------------------------------------------------------------------------- 1 | WAZUH_BRANCH=3.12 2 | WAZUH_API_BRANCH=3.12 3 | WAZUH_DOC_BRANCH=3.12 4 | MASTER_NAME=wazuh-master 5 | WORKER1_NAME=worker-1 6 | WORKER2_NAME=worker-2 7 | -------------------------------------------------------------------------------- /test/environment/docker/centos/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | services: 4 | wazuh-master: 5 | build: 6 | context: ./wazuh-manager 7 | args: 8 | wazuh_branch: "${WAZUH_BRANCH}" 9 | wazuh_api_branch: "${WAZUH_API_BRANCH}" 10 | image: wazuh-manager-centos:${WAZUH_BRANCH} 11 | volumes: 12 | - ./share:/share 13 | entrypoint: 14 | - /scripts/entrypoint.sh 15 | - ${MASTER_NAME} 16 | - master 17 | - master 18 | wazuh-worker1: 19 | build: 20 | context: ./wazuh-manager 21 | args: 22 | wazuh_branch: "${WAZUH_BRANCH}" 23 | wazuh_api_branch: "${WAZUH_API_BRANCH}" 24 | image: wazuh-manager-centos:${WAZUH_BRANCH} 25 | volumes: 26 | - ./share:/share 27 | depends_on: 28 | - wazuh-master 29 | entrypoint: 30 | - /scripts/entrypoint.sh 31 | - ${MASTER_NAME} 32 | - ${WORKER1_NAME} 33 | - worker 34 | wazuh-worker2: 35 | build: 36 | context: ./wazuh-manager 37 | args: 38 | wazuh_branch: "${WAZUH_BRANCH}" 39 | wazuh_api_branch: "${WAZUH_API_BRANCH}" 40 | image: wazuh-manager-centos:${WAZUH_BRANCH} 41 | depends_on: 42 | - wazuh-master 43 | entrypoint: 44 | - /scripts/entrypoint.sh 45 | - ${MASTER_NAME} 46 | - ${WORKER2_NAME} 47 | - worker 48 | wazuh-agent1: 49 | build: 50 | context: ./wazuh-agent 51 | image: wazuh-agent-centos:last-stable 52 | entrypoint: 53 | - /scripts/entrypoint.sh 54 | - wazuh-master 55 | - wazuh-master 56 | depends_on: 57 | - wazuh-master 58 | wazuh-agent2: 59 | build: 60 | context: ./wazuh-agent 61 | image: wazuh-agent-centos:last-stable 62 | entrypoint: 63 | - /scripts/entrypoint.sh 64 | - wazuh-master 65 | - wazuh-worker1 66 | depends_on: 67 | - wazuh-master 68 | - wazuh-worker1 69 | - wazuh-agent1 70 | wazuh-agent3: 71 | build: 72 | context: ./wazuh-agent-outdated 73 | image: wazuh-agent-centos:3.5 74 | entrypoint: 75 | - /scripts/entrypoint.sh 76 | - wazuh-master 77 | - wazuh-worker2 78 | depends_on: 79 | - wazuh-master 80 | - wazuh-agent1 81 | - wazuh-agent2 82 | -------------------------------------------------------------------------------- /test/environment/docker/centos/wazuh-agent-outdated/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:latest 2 | 3 | RUN yum install which -y 4 | 5 | RUN rpm --import http://packages.wazuh.com/key/GPG-KEY-WAZUH 6 | COPY wazuh-repository.txt /etc/yum.repos.d/wazuh.repo 7 | 8 | RUN yum install wazuh-agent-3.5.0 -y 9 | 10 | COPY entrypoint.sh /scripts/entrypoint.sh 11 | -------------------------------------------------------------------------------- /test/environment/docker/centos/wazuh-agent-outdated/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | sleep 5 4 | /var/ossec/bin/agent-auth -m $1 5 | sed -i "s:MANAGER_IP:$2:g" /var/ossec/etc/ossec.conf 6 | 7 | /var/ossec/bin/ossec-control stop 8 | /var/ossec/bin/ossec-control start 9 | tail -f /var/ossec/logs/ossec.log 10 | -------------------------------------------------------------------------------- /test/environment/docker/centos/wazuh-agent-outdated/wazuh-repository.txt: -------------------------------------------------------------------------------- 1 | [wazuh_repo] 2 | gpgcheck=1 3 | gpgkey=https://packages.wazuh.com/key/GPG-KEY-WAZUH 4 | enabled=1 5 | name=Wazuh repository 6 | baseurl=https://packages.wazuh.com/3.x/yum/ 7 | protect=1 -------------------------------------------------------------------------------- /test/environment/docker/centos/wazuh-agent/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:latest 2 | 3 | RUN yum install which -y 4 | 5 | RUN rpm --import http://packages.wazuh.com/key/GPG-KEY-WAZUH 6 | COPY wazuh-repository.txt /etc/yum.repos.d/wazuh.repo 7 | 8 | RUN yum install wazuh-agent -y 9 | 10 | COPY entrypoint.sh /scripts/entrypoint.sh 11 | -------------------------------------------------------------------------------- /test/environment/docker/centos/wazuh-agent/agent-ossec.conf: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 |
MANAGER_IP
11 | 1514 12 | udp 13 |
14 | rhel, rhel7 15 | 10 16 | 60 17 | yes 18 | aes 19 |
20 | 21 | 22 | 23 | no 24 | 5000 25 | 500 26 | 27 | 28 | 29 | 30 | no 31 | yes 32 | yes 33 | yes 34 | yes 35 | yes 36 | yes 37 | yes 38 | 39 | 40 | 43200 41 | 42 | /var/ossec/etc/shared/rootkit_files.txt 43 | /var/ossec/etc/shared/rootkit_trojans.txt 44 | 45 | yes 46 | 47 | 48 | 49 | yes 50 | 1800 51 | 1d 52 | yes 53 | 54 | 55 | xccdf_org.ssgproject.content_profile_pci-dss 56 | xccdf_org.ssgproject.content_profile_common 57 | 58 | 59 | 60 | 61 | 62 | yes 63 | 1800 64 | 1d 65 | yes 66 | 67 | wodles/java 68 | wodles/ciscat 69 | 70 | 71 | 72 | 73 | yes 74 | yes 75 | /var/log/osquery/osqueryd.results.log 76 | /etc/osquery/osquery.conf 77 | yes 78 | 79 | 80 | 81 | 82 | no 83 | 1h 84 | yes 85 | yes 86 | yes 87 | yes 88 | yes 89 | yes 90 | yes 91 | 92 | 93 | 94 | yes 95 | yes 96 | 12h 97 | yes 98 | 99 | 100 | cis_rhel7_linux_rcl.yml 101 | system_audit_rcl.yml 102 | system_audit_ssh.yml 103 | system_audit_pw.yml 104 | 105 | 106 | 107 | 108 | 109 | no 110 | 111 | 112 | 43200 113 | 114 | yes 115 | 116 | 117 | /etc,/usr/bin,/usr/sbin 118 | /bin,/sbin,/boot 119 | 120 | 121 | /etc/mtab 122 | /etc/hosts.deny 123 | /etc/mail/statistics 124 | /etc/random-seed 125 | /etc/random.seed 126 | /etc/adjtime 127 | /etc/httpd/logs 128 | /etc/utmpx 129 | /etc/wtmpx 130 | /etc/cups/certs 131 | /etc/dumpdates 132 | /etc/svc/volatile 133 | /sys/kernel/security 134 | /sys/kernel/debug 135 | /dev/core 136 | 137 | 138 | ^/proc 139 | .log$|.swp$ 140 | 141 | 142 | /etc/ssl/private.key 143 | 144 | yes 145 | 146 | 147 | 148 | 149 | command 150 | df -P 151 | 360 152 | 153 | 154 | 155 | full_command 156 | netstat -tulpn | sed 's/\([[:alnum:]]\+\)\ \+[[:digit:]]\+\ \+[[:digit:]]\+\ \+\(.*\):\([[:digit:]]*\)\ \+\([0-9\.\:\*]\+\).\+\ \([[:digit:]]*\/[[:alnum:]\-]*\).*/\1 \2 == \3 == \4 \5/' | sort -k 4 -g | sed 's/ == \(.*\) ==/:\1/' | sed 1,2d 157 | netstat listening ports 158 | 360 159 | 160 | 161 | 162 | full_command 163 | last -n 20 164 | 360 165 | 166 | 167 | 168 | 169 | no 170 | /var/ossec/etc/wpk_root.pem 171 | yes 172 | 173 | 174 | 175 | 176 | plain 177 | 178 | 179 |
180 | -------------------------------------------------------------------------------- /test/environment/docker/centos/wazuh-agent/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | sleep 5 4 | /var/ossec/bin/agent-auth -m $1 5 | sed -i "s:MANAGER_IP:$2:g" /var/ossec/etc/ossec.conf 6 | 7 | /var/ossec/bin/ossec-control stop 8 | /var/ossec/bin/ossec-control start 9 | 10 | tail -f /var/ossec/logs/ossec.log 11 | -------------------------------------------------------------------------------- /test/environment/docker/centos/wazuh-agent/wazuh-repository.txt: -------------------------------------------------------------------------------- 1 | [wazuh_repo] 2 | gpgcheck=1 3 | gpgkey=https://packages.wazuh.com/key/GPG-KEY-WAZUH 4 | enabled=1 5 | name=Wazuh repository 6 | baseurl=https://packages.wazuh.com/3.x/yum/ 7 | protect=1 -------------------------------------------------------------------------------- /test/environment/docker/centos/wazuh-manager/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:latest 2 | 3 | ARG wazuh_branch 4 | ARG wazuh_api_branch 5 | ARG wazuh_doc_branch 6 | 7 | # enable SSH 8 | RUN yum install openssl openssh-server -y 9 | RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config 10 | RUN echo 'root:wazuh_admin' | chpasswd 11 | RUN ssh-keygen -A 12 | 13 | EXPOSE 22 14 | 15 | # install dependencies 16 | RUN dnf install make gcc automake autoconf libtool git which sudo wget sqlite -y 17 | 18 | # install Python 3 19 | RUN dnf install python3 -y 20 | 21 | # install Wazuh 22 | RUN git clone https://github.com/wazuh/wazuh && cd /wazuh && git checkout ${wazuh_branch} 23 | # build Python dependencies 24 | RUN sed -i 's!--index-url=file://${ROUTE_PATH}/${EXTERNAL_CPYTHON}/Dependencies/simple!!' /wazuh/src/Makefile 25 | COPY preloaded-vars.conf /wazuh/etc/preloaded-vars.conf 26 | RUN /wazuh/install.sh 27 | 28 | # wazuh-documentation and dependencies 29 | RUN git clone https://github.com/wazuh/wazuh-documentation && cd /wazuh-documentation && git checkout ${wazuh_doc_branch} 30 | RUN pip3 install Sphinx==1.6.5 sphinx-rtd-theme==0.2.4 sphinxcontrib-images==0.7.0 sphinxprettysearchresults==0.3.5 31 | 32 | # install pip libraries for development 33 | RUN /var/ossec/framework/python/bin/pip3 install pytest ipython defusedxml ptvsd pydevd-pycharm~=191.6605.12 freezegun 34 | 35 | # install and configure Wazuh API 36 | RUN dnf install nodejs -y && npm config set user 0 37 | RUN git clone https://github.com/wazuh/wazuh-api && cd /wazuh-api && git checkout ${wazuh_api_branch} && ./install_api.sh && npm install mocha apidoc -g && npm install glob supertest mocha should moment mochawesome sqlite3 38 | 39 | # install ZSH 40 | RUN yum install zsh -y 41 | RUN cd /root && sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" 42 | 43 | # copy script for running mocha tests 44 | COPY run_mocha_tests.sh /wazuh-api 45 | 46 | # copy script for generating Wazuh API reference 47 | COPY generate_api_doc.sh /wazuh-api/doc/generate_api_doc.sh 48 | 49 | # copy entrypoint and configuration files 50 | COPY entrypoint.sh /scripts/entrypoint.sh 51 | COPY master-ossec.conf /scripts/master-ossec.conf 52 | COPY worker-ossec.conf /scripts/worker-ossec.conf 53 | -------------------------------------------------------------------------------- /test/environment/docker/centos/wazuh-manager/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ "X$3" == "Xmaster" ]; then 4 | cp /scripts/master-ossec.conf /var/ossec/etc/ossec.conf 5 | elif [ "X$3" == "Xworker" ]; then 6 | cp /scripts/worker-ossec.conf /var/ossec/etc/ossec.conf 7 | fi 8 | 9 | # add cluster configuration 10 | sed -i "s::9d273b53510fef702b54a92e9cffc82e:g" /var/ossec/etc/ossec.conf 11 | sed -i "s:NODE_IP:$1:g" /var/ossec/etc/ossec.conf 12 | sed -i -e "//,/<\/cluster>/ s|[a-z]\+|no|g" /var/ossec/etc/ossec.conf 13 | sed -i "s:node01:$2:g" /var/ossec/etc/ossec.conf 14 | 15 | if [ "X$3" != "Xmaster" ]; then 16 | sed -i "s:master:worker:g" /var/ossec/etc/ossec.conf 17 | fi 18 | 19 | # enable syscheck DB 20 | echo 'wazuh_database.sync_syscheck=1' >> /var/ossec/etc/local_internal_options.conf 21 | 22 | # configure API for development 23 | sed -i 's/config.logs = "info";/config.logs = "debug";/g' /var/ossec/api/configuration/config.js 24 | sed -i 's/config.cache_enabled = "yes";/config.cache_enabled = "no";/g' /var/ossec/api/configuration/config.js 25 | sed -i 's/config.experimental_features = false;/config.experimental_features = true;/g' /var/ossec/api/configuration/config.js 26 | cat <> /var/ossec/api/configuration/preloaded_vars.conf 27 | COUNTRY="US" 28 | STATE="State" 29 | LOCALITY="Locality" 30 | ORG_NAME="Org Name" 31 | ORG_UNIT="Org Unit Name" 32 | COMMON_NAME="Common Name" 33 | PASSWORD="password" 34 | USER=foo 35 | PASS=bar 36 | PORT=55000 37 | HTTPS=Y 38 | AUTH=Y 39 | PROXY=N 40 | EOT 41 | /var/ossec/api/scripts/configure_api.sh 42 | 43 | # start Wazuh 44 | /var/ossec/bin/ossec-control start 45 | 46 | # start Wazuh API 47 | node /var/ossec/api/app.js & 48 | 49 | # start SSH server 50 | /usr/sbin/sshd -D & 51 | 52 | tail -f /var/ossec/logs/ossec.log 53 | -------------------------------------------------------------------------------- /test/environment/docker/centos/wazuh-manager/generate_api_doc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | /var/ossec/framework/python/bin/python3 ./generate_rst.py /wazuh-documentation/source/user-manual/api/reference.rst 3 | cd /wazuh-documentation 4 | make html 5 | -------------------------------------------------------------------------------- /test/environment/docker/centos/wazuh-manager/preloaded-vars.conf: -------------------------------------------------------------------------------- 1 | USER_LANGUAGE="en" 2 | USER_NO_STOP="y" 3 | USER_INSTALL_TYPE="server" 4 | USER_DIR="/var/ossec" 5 | USER_ENABLE_EMAIL="n" 6 | USER_ENABLE_SYSCHECK="y" 7 | USER_ENABLE_ROOTCHECK="y" 8 | USER_ENABLE_OPENSCAP="y" 9 | USER_WHITE_LIST="n" 10 | USER_ENABLE_SYSLOG="y" 11 | USER_ENABLE_AUTHD="y" 12 | USER_AUTO_START="y" 13 | 14 | -------------------------------------------------------------------------------- /test/environment/docker/centos/wazuh-manager/run_mocha_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | TEST_PATH=./test 4 | TIMEOUT=10000 5 | REPORTER=./node_modules/mochawesome 6 | REPORT_DIR=/share 7 | ARGS="--timeout=${TIMEOUT} --reporter ${REPORTER} --reporter-options reportDir=${REPORT_DIR},reportFilename=" 8 | 9 | REPORT_NAME=active_response 10 | mocha ${TEST_PATH}/test_active_response.js ${ARGS}${REPORT_NAME} 11 | 12 | REPORT_NAME=agents 13 | mocha ${TEST_PATH}/test_agents.js ${ARGS}${REPORT_NAME} 14 | sleep 20 15 | 16 | REPORT_NAME=agents2 17 | mocha ${TEST_PATH}/test_agents_2.js ${ARGS}${REPORT_NAME} 18 | 19 | REPORT_NAME=app 20 | mocha ${TEST_PATH}/test_app.js ${ARGS}${REPORT_NAME} 21 | 22 | REPORT_NAME=cluster 23 | mocha ${TEST_PATH}/test_cluster.js ${ARGS}${REPORT_NAME} 24 | sleep 20 25 | 26 | REPORT_NAME=decoders 27 | mocha ${TEST_PATH}/test_decoders.js ${ARGS}${REPORT_NAME} 28 | 29 | REPORT_NAME=lists 30 | mocha ${TEST_PATH}/test_lists.js ${ARGS}${REPORT_NAME} 31 | 32 | REPORT_NAME=manager 33 | mocha ${TEST_PATH}/test_manager.js ${ARGS}${REPORT_NAME} 34 | sleep 20 35 | 36 | REPORT_NAME=mitre 37 | mocha ${TEST_PATH}/test_mitre.js ${ARGS}${REPORT_NAME} 38 | 39 | REPORT_NAME=rootcheck 40 | mocha ${TEST_PATH}/test_rootcheck.js ${ARGS}${REPORT_NAME} 41 | 42 | REPORT_NAME=rules 43 | mocha ${TEST_PATH}/test_rules.js ${ARGS}${REPORT_NAME} 44 | 45 | REPORT_NAME=sca 46 | mocha ${TEST_PATH}/test_sca.js ${ARGS}${REPORT_NAME} 47 | 48 | REPORT_NAME=summary 49 | ARGS=$ARGS 50 | mocha ${TEST_PATH}/test_summary.js ${ARGS}${REPORT_NAME} 51 | 52 | REPORT_NAME=syscheck 53 | ARGS=$ARGS 54 | mocha ${TEST_PATH}/test_syscheck.js ${ARGS}${REPORT_NAME} 55 | 56 | REPORT_NAME=syscollector 57 | mocha ${TEST_PATH}/test_syscollector.js ${ARGS}${REPORT_NAME} 58 | 59 | chmod +r ${TEST_PATH}/* 60 | -------------------------------------------------------------------------------- /test/environment/docker/ubuntu/.env: -------------------------------------------------------------------------------- 1 | WAZUH_BRANCH=3.13 2 | WAZUH_API_BRANCH=3.13 3 | WAZUH_DOC_BRANCH=3.13 4 | MASTER_NAME=wazuh-master 5 | WORKER1_NAME=worker-1 6 | WORKER2_NAME=worker-2 7 | -------------------------------------------------------------------------------- /test/environment/docker/ubuntu/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | services: 4 | wazuh-master: 5 | build: 6 | context: ./wazuh-manager 7 | args: 8 | wazuh_branch: "${WAZUH_BRANCH}" 9 | wazuh_api_branch: "${WAZUH_API_BRANCH}" 10 | image: wazuh-manager-ubuntu:${WAZUH_BRANCH} 11 | volumes: 12 | - ./share:/share 13 | entrypoint: 14 | - /scripts/entrypoint.sh 15 | - ${MASTER_NAME} 16 | - master 17 | - master 18 | wazuh-worker1: 19 | build: 20 | context: ./wazuh-manager 21 | args: 22 | wazuh_branch: "${WAZUH_BRANCH}" 23 | wazuh_api_branch: "${WAZUH_API_BRANCH}" 24 | image: wazuh-manager-ubuntu:${WAZUH_BRANCH} 25 | volumes: 26 | - ./share:/share 27 | depends_on: 28 | - wazuh-master 29 | entrypoint: 30 | - /scripts/entrypoint.sh 31 | - ${MASTER_NAME} 32 | - ${WORKER1_NAME} 33 | - worker 34 | wazuh-worker2: 35 | build: 36 | context: ./wazuh-manager 37 | args: 38 | wazuh_branch: "${WAZUH_BRANCH}" 39 | wazuh_api_branch: "${WAZUH_API_BRANCH}" 40 | image: wazuh-manager-ubuntu:${WAZUH_BRANCH} 41 | depends_on: 42 | - wazuh-master 43 | entrypoint: 44 | - /scripts/entrypoint.sh 45 | - ${MASTER_NAME} 46 | - ${WORKER2_NAME} 47 | - worker 48 | wazuh-agent1: 49 | build: 50 | context: ./wazuh-agent 51 | image: wazuh-agent-ubuntu:last-stable 52 | entrypoint: 53 | - /scripts/entrypoint.sh 54 | - wazuh-master 55 | - wazuh-master 56 | depends_on: 57 | - wazuh-master 58 | wazuh-agent2: 59 | build: 60 | context: ./wazuh-agent 61 | image: wazuh-agent-ubuntu:last-stable 62 | entrypoint: 63 | - /scripts/entrypoint.sh 64 | - wazuh-master 65 | - wazuh-worker1 66 | depends_on: 67 | - wazuh-master 68 | - wazuh-worker1 69 | - wazuh-agent1 70 | wazuh-agent3: 71 | build: 72 | context: ./wazuh-agent-outdated 73 | image: wazuh-agent-ubuntu:3.5 74 | entrypoint: 75 | - /scripts/entrypoint.sh 76 | - wazuh-master 77 | - wazuh-worker2 78 | depends_on: 79 | - wazuh-master 80 | - wazuh-agent1 81 | - wazuh-agent2 82 | -------------------------------------------------------------------------------- /test/environment/docker/ubuntu/wazuh-agent-outdated/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | 3 | RUN apt-get update && apt-get install git curl apt-transport-https lsb-release gnupg2 -y && \ 4 | curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | apt-key add - && \ 5 | echo "deb https://packages.wazuh.com/3.x/apt/ stable main" | tee /etc/apt/sources.list.d/wazuh.list && \ 6 | apt-get update 7 | 8 | RUN apt-get install wazuh-agent=3.5.0-1 -y 9 | 10 | COPY entrypoint.sh /scripts/entrypoint.sh 11 | -------------------------------------------------------------------------------- /test/environment/docker/ubuntu/wazuh-agent-outdated/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | sleep 5 4 | /var/ossec/bin/agent-auth -m $1 5 | sed -i "s:MANAGER_IP:$2:g" /var/ossec/etc/ossec.conf 6 | 7 | /var/ossec/bin/ossec-control stop 8 | /var/ossec/bin/ossec-control start 9 | tail -f /var/ossec/logs/ossec.log 10 | -------------------------------------------------------------------------------- /test/environment/docker/ubuntu/wazuh-agent/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | 3 | RUN apt-get update && apt-get install git curl apt-transport-https lsb-release gnupg2 -y && \ 4 | curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | apt-key add - && \ 5 | echo "deb https://packages.wazuh.com/3.x/apt/ stable main" | tee /etc/apt/sources.list.d/wazuh.list && \ 6 | apt-get update 7 | 8 | RUN apt-get install wazuh-agent -y 9 | 10 | COPY entrypoint.sh /scripts/entrypoint.sh 11 | COPY agent-ossec.conf /var/ossec/etc/ossec.conf 12 | -------------------------------------------------------------------------------- /test/environment/docker/ubuntu/wazuh-agent/agent-ossec.conf: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 |
MANAGER_IP
11 | 1514 12 | udp 13 |
14 | ubuntu, ubuntu18, ubuntu18.04 15 | 10 16 | 60 17 | yes 18 | aes 19 |
20 | 21 | 22 | 23 | no 24 | 5000 25 | 500 26 | 27 | 28 | 29 | 30 | no 31 | yes 32 | yes 33 | yes 34 | yes 35 | yes 36 | yes 37 | yes 38 | 39 | 40 | 43200 41 | 42 | /var/ossec/etc/shared/rootkit_files.txt 43 | /var/ossec/etc/shared/rootkit_trojans.txt 44 | 45 | yes 46 | 47 | 48 | 49 | yes 50 | 1800 51 | 1d 52 | yes 53 | 54 | 55 | 56 | yes 57 | 1800 58 | 1d 59 | yes 60 | 61 | wodles/java 62 | wodles/ciscat 63 | 64 | 65 | 66 | 67 | yes 68 | yes 69 | /var/log/osquery/osqueryd.results.log 70 | /etc/osquery/osquery.conf 71 | yes 72 | 73 | 74 | 75 | 76 | no 77 | 1h 78 | yes 79 | yes 80 | yes 81 | yes 82 | yes 83 | yes 84 | yes 85 | 86 | 87 | 88 | yes 89 | yes 90 | 12h 91 | yes 92 | 93 | 94 | cis_debian_linux_rcl.yml 95 | system_audit_rcl.yml 96 | system_audit_ssh.yml 97 | system_audit_pw.yml 98 | 99 | 100 | 101 | 102 | 103 | no 104 | 105 | 106 | 43200 107 | 108 | yes 109 | 110 | 111 | /etc,/usr/bin,/usr/sbin 112 | /bin,/sbin,/boot 113 | 114 | 115 | /etc/mtab 116 | /etc/hosts.deny 117 | /etc/mail/statistics 118 | /etc/random-seed 119 | /etc/random.seed 120 | /etc/adjtime 121 | /etc/httpd/logs 122 | /etc/utmpx 123 | /etc/wtmpx 124 | /etc/cups/certs 125 | /etc/dumpdates 126 | /etc/svc/volatile 127 | /sys/kernel/security 128 | /sys/kernel/debug 129 | /dev/core 130 | 131 | 132 | ^/proc 133 | .log$|.swp$ 134 | 135 | 136 | /etc/ssl/private.key 137 | 138 | yes 139 | 140 | 141 | 142 | 143 | command 144 | df -P 145 | 360 146 | 147 | 148 | 149 | full_command 150 | netstat -tulpn | sed 's/\([[:alnum:]]\+\)\ \+[[:digit:]]\+\ \+[[:digit:]]\+\ \+\(.*\):\([[:digit:]]*\)\ \+\([0-9\.\:\*]\+\).\+\ \([[:digit:]]*\/[[:alnum:]\-]*\).*/\1 \2 == \3 == \4 \5/' | sort -k 4 -g | sed 's/ == \(.*\) ==/:\1/' | sed 1,2d 151 | netstat listening ports 152 | 360 153 | 154 | 155 | 156 | full_command 157 | last -n 20 158 | 360 159 | 160 | 161 | 162 | 163 | no 164 | /var/ossec/etc/wpk_root.pem 165 | yes 166 | 167 | 168 | 169 | 170 | plain 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 |
-------------------------------------------------------------------------------- /test/environment/docker/ubuntu/wazuh-agent/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | sleep 5 4 | /var/ossec/bin/agent-auth -m $1 5 | sed -i "s:MANAGER_IP:$2:g" /var/ossec/etc/ossec.conf 6 | 7 | /var/ossec/bin/ossec-control stop 8 | /var/ossec/bin/ossec-control start 9 | 10 | tail -f /var/ossec/logs/ossec.log 11 | -------------------------------------------------------------------------------- /test/environment/docker/ubuntu/wazuh-manager/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | ARG wazuh_branch 4 | ARG wazuh_api_branch 5 | ARG wazuh_doc_branch 6 | 7 | # enable SSH 8 | RUN apt-get update && apt-get install -y openssh-server 9 | RUN mkdir /var/run/sshd 10 | RUN echo 'root:wazuh_admin' | chpasswd 11 | RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config 12 | 13 | # SSH login fix. Otherwise user is kicked off after login 14 | RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd 15 | 16 | ENV NOTVISIBLE "in users profile" 17 | RUN echo "export VISIBLE=now" >> /etc/profile 18 | 19 | EXPOSE 22 20 | 21 | # install dependencies 22 | RUN apt-get install python python-pip python3 python3-pip git gnupg2 gcc make vim libc6-dev curl policycoreutils automake autoconf libtool apt-transport-https lsb-release sqlite3 sudo -y 23 | 24 | # install Wazuh 25 | RUN git clone https://github.com/wazuh/wazuh && cd /wazuh && git checkout ${wazuh_branch} 26 | # build Python dependencies 27 | RUN sed -i 's!--index-url=file://${ROUTE_PATH}/${EXTERNAL_CPYTHON}/Dependencies/simple!!' /wazuh/src/Makefile 28 | COPY preloaded-vars.conf /wazuh/etc/preloaded-vars.conf 29 | RUN /wazuh/install.sh 30 | 31 | # wazuh-documentation and dependencies 32 | RUN git clone https://github.com/wazuh/wazuh-documentation && cd /wazuh-documentation && git checkout ${wazuh_doc_branch} 33 | RUN pip3 install Sphinx==1.6.5 sphinx-rtd-theme==0.2.4 sphinxcontrib-images==0.7.0 sphinxprettysearchresults==0.3.5 34 | 35 | # install pip libraries for development 36 | RUN /var/ossec/framework/python/bin/pip3 install pytest defusedxml ptvsd pydevd-pycharm~=191.6605.12 freezegun 37 | 38 | # install and configure Wazuh API 39 | RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt-get install -y nodejs && npm config set user 0 40 | RUN git clone https://github.com/wazuh/wazuh-api && cd /wazuh-api && git checkout ${wazuh_api_branch} && ./install_api.sh && npm install mocha apidoc -g && npm install glob supertest mocha should moment mochawesome sqlite3 41 | 42 | # install ZSH 43 | RUN apt-get install zsh -y 44 | RUN cd /root && sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" 45 | 46 | # copy script for running mocha tests 47 | COPY run_mocha_tests.sh /wazuh-api 48 | 49 | # copy script for generating Wazuh API reference 50 | COPY generate_api_doc.sh /wazuh-api/doc/generate_api_doc.sh 51 | 52 | # copy entrypoint and configuration files 53 | COPY entrypoint.sh /scripts/entrypoint.sh 54 | COPY master-ossec.conf /scripts/master-ossec.conf 55 | COPY worker-ossec.conf /scripts/worker-ossec.conf 56 | -------------------------------------------------------------------------------- /test/environment/docker/ubuntu/wazuh-manager/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ "X$3" == "Xmaster" ]; then 4 | cp /scripts/master-ossec.conf /var/ossec/etc/ossec.conf 5 | elif [ "X$3" == "Xworker" ]; then 6 | cp /scripts/worker-ossec.conf /var/ossec/etc/ossec.conf 7 | fi 8 | 9 | # add cluster configuration 10 | sed -i "s::9d273b53510fef702b54a92e9cffc82e:g" /var/ossec/etc/ossec.conf 11 | sed -i "s:NODE_IP:$1:g" /var/ossec/etc/ossec.conf 12 | sed -i -e "//,/<\/cluster>/ s|[a-z]\+|no|g" /var/ossec/etc/ossec.conf 13 | sed -i "s:node01:$2:g" /var/ossec/etc/ossec.conf 14 | 15 | if [ "X$3" != "Xmaster" ]; then 16 | sed -i "s:master:worker:g" /var/ossec/etc/ossec.conf 17 | fi 18 | 19 | # enable syscheck DB 20 | echo 'wazuh_database.sync_syscheck=1' >> /var/ossec/etc/local_internal_options.conf 21 | 22 | # configure API for development 23 | sed -i 's/config.logs = "info";/config.logs = "debug";/g' /var/ossec/api/configuration/config.js 24 | sed -i 's/config.cache_enabled = "yes";/config.cache_enabled = "no";/g' /var/ossec/api/configuration/config.js 25 | sed -i 's/config.experimental_features = false;/config.experimental_features = true;/g' /var/ossec/api/configuration/config.js 26 | cat <> /var/ossec/api/configuration/preloaded_vars.conf 27 | COUNTRY="US" 28 | STATE="State" 29 | LOCALITY="Locality" 30 | ORG_NAME="Org Name" 31 | ORG_UNIT="Org Unit Name" 32 | COMMON_NAME="Common Name" 33 | PASSWORD="password" 34 | USER=foo 35 | PASS=bar 36 | PORT=55000 37 | HTTPS=Y 38 | AUTH=Y 39 | PROXY=N 40 | EOT 41 | /var/ossec/api/scripts/configure_api.sh 42 | 43 | # fix for generating Wazuh documentation 44 | sed -i '26 s/self.errors/'\''ignore'\''/g' /usr/lib/python3.6/encodings/ascii.py 45 | 46 | # start Wazuh 47 | /var/ossec/bin/ossec-control start 48 | 49 | # start Wazuh API 50 | node /var/ossec/api/app.js & 51 | 52 | # start SSH server 53 | /usr/sbin/sshd -D & 54 | 55 | tail -f /var/ossec/logs/ossec.log 56 | 57 | -------------------------------------------------------------------------------- /test/environment/docker/ubuntu/wazuh-manager/generate_api_doc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | /var/ossec/framework/python/bin/python3 ./generate_rst.py /wazuh-documentation/source/user-manual/api/reference.rst 3 | cd /wazuh-documentation 4 | make html 5 | -------------------------------------------------------------------------------- /test/environment/docker/ubuntu/wazuh-manager/preloaded-vars.conf: -------------------------------------------------------------------------------- 1 | USER_LANGUAGE="en" 2 | USER_NO_STOP="y" 3 | USER_INSTALL_TYPE="server" 4 | USER_DIR="/var/ossec" 5 | USER_ENABLE_EMAIL="n" 6 | USER_ENABLE_SYSCHECK="y" 7 | USER_ENABLE_ROOTCHECK="y" 8 | USER_ENABLE_OPENSCAP="y" 9 | USER_WHITE_LIST="n" 10 | USER_ENABLE_SYSLOG="y" 11 | USER_ENABLE_AUTHD="y" 12 | USER_AUTO_START="y" 13 | 14 | -------------------------------------------------------------------------------- /test/environment/docker/ubuntu/wazuh-manager/run_mocha_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | TEST_PATH=./test 4 | TIMEOUT=10000 5 | REPORTER=./node_modules/mochawesome 6 | REPORT_DIR=/share 7 | ARGS="--timeout=${TIMEOUT} --reporter ${REPORTER} --reporter-options reportDir=${REPORT_DIR},reportFilename=" 8 | 9 | REPORT_NAME=active_response 10 | mocha ${TEST_PATH}/test_active_response.js ${ARGS}${REPORT_NAME} 11 | 12 | REPORT_NAME=agents 13 | mocha ${TEST_PATH}/test_agents.js ${ARGS}${REPORT_NAME} 14 | sleep 20 15 | 16 | REPORT_NAME=agents2 17 | mocha ${TEST_PATH}/test_agents_2.js ${ARGS}${REPORT_NAME} 18 | 19 | REPORT_NAME=app 20 | mocha ${TEST_PATH}/test_app.js ${ARGS}${REPORT_NAME} 21 | 22 | REPORT_NAME=cluster 23 | mocha ${TEST_PATH}/test_cluster.js ${ARGS}${REPORT_NAME} 24 | sleep 20 25 | 26 | REPORT_NAME=decoders 27 | mocha ${TEST_PATH}/test_decoders.js ${ARGS}${REPORT_NAME} 28 | 29 | REPORT_NAME=lists 30 | mocha ${TEST_PATH}/test_lists.js ${ARGS}${REPORT_NAME} 31 | 32 | REPORT_NAME=manager 33 | mocha ${TEST_PATH}/test_manager.js ${ARGS}${REPORT_NAME} 34 | sleep 20 35 | 36 | REPORT_NAME=mitre 37 | mocha ${TEST_PATH}/test_mitre.js ${ARGS}${REPORT_NAME} 38 | 39 | REPORT_NAME=rootcheck 40 | mocha ${TEST_PATH}/test_rootcheck.js ${ARGS}${REPORT_NAME} 41 | 42 | REPORT_NAME=rules 43 | mocha ${TEST_PATH}/test_rules.js ${ARGS}${REPORT_NAME} 44 | 45 | REPORT_NAME=sca 46 | mocha ${TEST_PATH}/test_sca.js ${ARGS}${REPORT_NAME} 47 | 48 | REPORT_NAME=summary 49 | ARGS=$ARGS 50 | mocha ${TEST_PATH}/test_summary.js ${ARGS}${REPORT_NAME} 51 | 52 | REPORT_NAME=syscheck 53 | ARGS=$ARGS 54 | mocha ${TEST_PATH}/test_syscheck.js ${ARGS}${REPORT_NAME} 55 | 56 | REPORT_NAME=syscollector 57 | mocha ${TEST_PATH}/test_syscollector.js ${ARGS}${REPORT_NAME} 58 | 59 | chmod +r ${TEST_PATH}/* 60 | -------------------------------------------------------------------------------- /test/environment/vagrant/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | master_ip = "172.19.0.100" 4 | workers_net = "172.19.0.10" # 172.17.0.10x 5 | agents_net = "172.19.0.20" # 172.17.0.20x 6 | n_workers = 1 7 | n_agents = 2 8 | box = "ubuntu/bionic64" 9 | wazuh_api_folder = ENV['WAZUH_API_PATH'] 10 | 11 | Vagrant.configure("2") do |config| 12 | # Manager master 13 | config.vm.define "master" do |node| 14 | node.vm.box = "#{box}" 15 | node.vm.synced_folder "#{wazuh_api_folder}", "/home/vagrant/wazuh_api" 16 | node.vm.provision :shell, path: "configure_manager.sh", args: "#{master_ip} master master /home/vagrant/wazuh_api" 17 | node.vm.network :private_network, ip: "#{master_ip}" 18 | node.vm.provider "virtualbox" do |pmv| 19 | pmv.memory = 512 20 | pmv.cpus = 1 21 | end 22 | node.vm.hostname = "master" 23 | end 24 | # Manager worker 25 | (1..n_workers).each do |i| 26 | config.vm.define "worker-#{i}" do |node| 27 | node.vm.box = "#{box}" 28 | node.vm.synced_folder "#{wazuh_api_folder}", "/home/vagrant/wazuh_api" 29 | node.vm.provision :shell, path: "configure_manager.sh", args: "#{master_ip} worker worker-#{i}" 30 | node.vm.network :private_network, ip: "#{workers_net}#{i}" 31 | node.vm.provider "virtualbox" do |pmv| 32 | pmv.memory = 512 33 | pmv.cpus = 1 34 | end 35 | node.vm.hostname = "worker-#{i}" 36 | end 37 | end 38 | # Agents 39 | (1..n_agents).each do |i| 40 | config.vm.define "agent-#{i}" do |node| 41 | node.vm.box = "#{box}" 42 | node.vm.provision :shell, path: "install_agent.sh", args: "#{master_ip} #{workers_net}1 agent-#{i} pre-release" 43 | node.vm.network :private_network, ip: "#{agents_net}#{i}" 44 | node.vm.provider "virtualbox" do |pmv| 45 | pmv.memory = 256 46 | pmv.cpus = 1 47 | end 48 | node.vm.hostname = "agent-#{i}" 49 | end 50 | end 51 | 52 | # Outdate agent 53 | config.vm.define "agent-outdated" do |node| 54 | node.vm.box = "#{box}" 55 | node.vm.provision :shell, path: "install_agent.sh", args: "#{master_ip} #{workers_net}1 agent-outdated stable" 56 | node.vm.network :private_network, ip: "#{agents_net}9" 57 | node.vm.provider "virtualbox" do |pmv| 58 | pmv.memory = 256 59 | pmv.cpus = 1 60 | end 61 | node.vm.hostname = "agent-outdated" 62 | end 63 | end 64 | 65 | -------------------------------------------------------------------------------- /test/environment/vagrant/configure_manager.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | master_ip=$1 4 | manager_type=$2 5 | node_name=$3 6 | wazuh_api_folder=$4 7 | 8 | apt-get update 9 | curl -s https://s3-us-west-1.amazonaws.com/packages-dev.wazuh.com/key/GPG-KEY-WAZUH | apt-key add - 10 | echo "deb https://s3-us-west-1.amazonaws.com/packages-dev.wazuh.com/pre-release/apt/ unstable main" | tee -a /etc/apt/sources.list.d/wazuh_pre_release.list 11 | 12 | apt-get update 13 | apt-get install wazuh-manager -y 14 | apt-get install python python-cryptography -y 15 | 16 | curl -sL https://deb.nodesource.com/setup_8.x | bash - 17 | apt-get install nodejs gcc -y 18 | npm install mocha -g 19 | npm install glob supertest mocha should moment 20 | npm config set user 0 21 | cd ${wazuh_api_folder} 22 | ${wazuh_api_folder}/install_api.sh 23 | API_CONF_FOLDER=/var/ossec/api/configuration 24 | PRECONF_FILE=${API_CONF_FOLDER}/preloaded_vars.conf 25 | cat <> ${PRECONF_FILE} 26 | HTTPS=Y 27 | AUTH=Y 28 | COUNTRY="US" 29 | STATE="State" 30 | LOCALITY="Locality" 31 | ORG_NAME="Org Name" 32 | ORG_UNIT="Org Unit Name" 33 | COMMON_NAME="Common Name" 34 | PASSWORD="password" 35 | USER=foo 36 | PASS=bar 37 | PORT=55000 38 | PROXY=N 39 | EOT 40 | /var/ossec/api/scripts/configure_api.sh 41 | 42 | sed -i "s:config.experimental_features = false;:config.experimental_features = true;:g" /var/ossec/api/configuration/config.js 43 | sed -i "s:config.cache_enabled = \"yes\";:config.cache_enabled = \"no\";:g" /var/ossec/api/configuration/config.js 44 | 45 | systemctl restart wazuh-api 46 | 47 | /var/ossec/bin/ossec-control enable agentless 48 | /var/ossec/bin/ossec-control enable client-syslog 49 | /var/ossec/bin/ossec-control enable integrator 50 | 51 | if [ "X${manager_type}" = "Xmaster" ] 52 | then 53 | cat << EOT >> /var/ossec/etc/local_internal_options.conf 54 | wazuh_database.sync_syscheck=1 55 | EOT 56 | 57 | cp /vagrant/ossec_master.conf /var/ossec/etc/ossec.conf 58 | else 59 | sed -i "s:master:worker:g" /var/ossec/etc/ossec.conf 60 | fi 61 | 62 | sed -i "s::9d273b53510fef702b54a92e9cffc82e:g" /var/ossec/etc/ossec.conf 63 | sed -i "s:NODE_IP:$master_ip:g" /var/ossec/etc/ossec.conf 64 | sed -i -e "//,/<\/cluster>/ s|[a-z]\+|no|g" /var/ossec/etc/ossec.conf 65 | sed -i "s:node01:$node_name:g" /var/ossec/etc/ossec.conf 66 | systemctl restart wazuh-manager 67 | 68 | if [ "X${manager_type}" = "Xmaster" ] 69 | then 70 | /var/ossec/bin/ossec-maild 71 | /var/ossec/bin/ossec-authd 72 | fi 73 | 74 | echo "Configure OK" 75 | -------------------------------------------------------------------------------- /test/environment/vagrant/install_agent.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | master_ip=$1 4 | worker_ip=$2 5 | agent_name=$3 6 | repo=$4 7 | 8 | apt-get update 9 | apt-get install -y curl apt-transport-https lsb-release 10 | 11 | curl -s https://s3-us-west-1.amazonaws.com/packages-dev.wazuh.com/key/GPG-KEY-WAZUH | apt-key add - 12 | if [ "X$repo" = "Xpre-release" ] 13 | then 14 | echo "deb https://s3-us-west-1.amazonaws.com/packages-dev.wazuh.com/pre-release/apt/ unstable main" | tee -a /etc/apt/sources.list.d/wazuh_pre_release.list 15 | apt-get update 16 | apt-get install -y wazuh-agent 17 | else 18 | echo "deb https://packages.wazuh.com/3.x/apt/ stable main" | tee -a /etc/apt/sources.list.d/wazuh.list 19 | apt-get update 20 | apt-get install -y wazuh-agent=3.5.0-1 21 | fi 22 | 23 | cp /vagrant/ossec_agents.conf /var/ossec/etc/ossec.conf 24 | sed -i "s:MANAGER_IP:$master_ip:g" /var/ossec/etc/ossec.conf 25 | /var/ossec/bin/agent-auth -m $master_ip -A $agent_name 26 | systemctl restart wazuh-agent 27 | -------------------------------------------------------------------------------- /test/environment/vagrant/ossec_agents.conf: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 |
MANAGER_IP
11 | 1514 12 | udp 13 |
14 | ubuntu, ubuntu18, ubuntu18.04 15 | 10 16 | 60 17 | yes 18 | aes 19 |
20 | 21 | 22 | 23 | no 24 | 5000 25 | 500 26 | 27 | 28 | 29 | 30 | no 31 | yes 32 | yes 33 | yes 34 | yes 35 | yes 36 | yes 37 | yes 38 | yes 39 | 40 | 41 | 43200 42 | 43 | /var/ossec/etc/shared/rootkit_files.txt 44 | /var/ossec/etc/shared/rootkit_trojans.txt 45 | 46 | /var/ossec/etc/shared/system_audit_rcl.txt 47 | /var/ossec/etc/shared/system_audit_ssh.txt 48 | 49 | yes 50 | 51 | 52 | 53 | yes 54 | 1800 55 | 1d 56 | yes 57 | 58 | 59 | 60 | yes 61 | 1800 62 | 1d 63 | yes 64 | 65 | wodles/java 66 | wodles/ciscat 67 | 68 | 69 | 70 | 71 | yes 72 | yes 73 | /var/log/osquery/osqueryd.results.log 74 | /etc/osquery/osquery.conf 75 | yes 76 | 77 | 78 | 79 | 80 | no 81 | 1h 82 | yes 83 | yes 84 | yes 85 | yes 86 | yes 87 | yes 88 | yes 89 | 90 | 91 | 92 | 93 | no 94 | 95 | 96 | 43200 97 | 98 | yes 99 | 100 | 101 | /etc,/usr/bin,/usr/sbin 102 | /bin,/sbin,/boot 103 | 104 | 105 | /etc/mtab 106 | /etc/hosts.deny 107 | /etc/mail/statistics 108 | /etc/random-seed 109 | /etc/random.seed 110 | /etc/adjtime 111 | /etc/httpd/logs 112 | /etc/utmpx 113 | /etc/wtmpx 114 | /etc/cups/certs 115 | /etc/dumpdates 116 | /etc/svc/volatile 117 | /sys/kernel/security 118 | /sys/kernel/debug 119 | 120 | 121 | .log$|.swp$ 122 | 123 | 124 | /etc/ssl/private.key 125 | 126 | yes 127 | 128 | 129 | yes 130 | 131 | 132 | 133 | 134 | command 135 | df -P 136 | 360 137 | 138 | 139 | 140 | full_command 141 | netstat -tulpn | sed 's/\([[:alnum:]]\+\)\ \+[[:digit:]]\+\ \+[[:digit:]]\+\ \+\(.*\):\([[:digit:]]*\)\ \+\([0-9\.\:\*]\+\).\+\ \([[:digit:]]*\/[[:alnum:]\-]*\).*/\1 \2 == \3 == \4 \5/' | sort -k 4 -g | sed 's/ == \(.*\) ==/:\1/' | sed 1,2d 142 | netstat listening ports 143 | 360 144 | 145 | 146 | 147 | full_command 148 | last -n 20 149 | 360 150 | 151 | 152 | 153 | 154 | no 155 | /var/ossec/etc/wpk_root.pem 156 | yes 157 | 158 | 159 | 160 | 161 | plain 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 |
173 | 174 | 175 | 176 | syslog 177 | /var/ossec/logs/active-responses.log 178 | 179 | 180 | 181 | syslog 182 | /var/log/auth.log 183 | 184 | 185 | 186 | syslog 187 | /var/log/syslog 188 | 189 | 190 | 191 | syslog 192 | /var/log/dpkg.log 193 | 194 | 195 | 196 | syslog 197 | /var/log/kern.log 198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /test/test_app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * API RESTful for OSSEC 3 | * Copyright (C) 2015-2016 Wazuh, Inc.All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | var should = require('should'); 13 | var assert = require('assert'); 14 | var request = require('supertest'); 15 | var common = require('./common.js'); 16 | 17 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; 18 | 19 | describe('App', function() { 20 | 21 | describe('Authentication', function() { 22 | it('Wrong USER', function(done) { 23 | request(common.url) 24 | .get("/") 25 | .auth('random', common.credentials.password) 26 | .expect("Content-type","text/plain") 27 | .expect(401) 28 | .end(function(err,res){ 29 | if (err) return done(err); 30 | done(); 31 | }); 32 | }); 33 | 34 | it('Wrong password', function(done) { 35 | request(common.url) 36 | .get("/") 37 | .auth(common.credentials.user, 'random') 38 | .expect("Content-type","text/plain") 39 | .expect(401) 40 | .end(function(err,res){ 41 | if (err) return done(err); 42 | done(); 43 | }); 44 | }); 45 | 46 | it('Home', function(done) { 47 | request(common.url) 48 | .get("/") 49 | .auth(common.credentials.user, common.credentials.password) 50 | .expect("Content-type",/json/) 51 | .expect(200) 52 | .end(function(err,res){ 53 | if (err) return done(err); 54 | 55 | res.body.should.have.properties(['error', 'data']); 56 | res.body.error.should.equal(0); 57 | done(); 58 | }); 59 | }); 60 | 61 | }); 62 | 63 | describe('Requests', function() { 64 | 65 | it('Inexistent request', function(done) { 66 | request(common.url) 67 | .get("/random") 68 | .auth(common.credentials.user, common.credentials.password) 69 | .expect("Content-type",/json/) 70 | .expect(404) 71 | .end(function(err,res){ 72 | if (err) return done(err); 73 | 74 | res.body.should.have.properties(['error', 'message']); 75 | res.body.error.should.equal(603); 76 | done(); 77 | }); 78 | }); 79 | 80 | it('API version', function(done) { 81 | request(common.url) 82 | .get("/version") 83 | .auth(common.credentials.user, common.credentials.password) 84 | .expect("Content-type",/json/) 85 | .expect(200) 86 | .end(function(err,res){ 87 | if (err) return done(err); 88 | 89 | res.body.should.have.properties(['error', 'data']); 90 | res.body.error.should.equal(0); 91 | res.body.data.should.be.type('string'); 92 | done(); 93 | }); 94 | }); 95 | 96 | }); 97 | }); 98 | -------------------------------------------------------------------------------- /test/test_rootcheck.js: -------------------------------------------------------------------------------- 1 | /** 2 | * API RESTful for OSSEC 3 | * Copyright (C) 2015-2016 Wazuh, Inc.All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | var should = require('should'); 13 | var assert = require('assert'); 14 | var request = require('supertest'); 15 | var common = require('./common.js'); 16 | 17 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; 18 | 19 | 20 | 21 | 22 | describe('Rootcheck', function() { 23 | 24 | describe('GET/rootcheck/:agent_id/last_scan', function() { 25 | 26 | it('Request', function(done) { 27 | request(common.url) 28 | .get("/rootcheck/000/last_scan") 29 | .auth(common.credentials.user, common.credentials.password) 30 | .expect("Content-type",/json/) 31 | .expect(200) 32 | .end(function(err,res){ 33 | if (err) return done(err); 34 | 35 | res.body.should.have.properties(['error', 'data']); 36 | 37 | res.body.error.should.equal(0); 38 | res.body.data.should.be.an.Object; 39 | res.body.data.should.have.properties(['end', 'start']); 40 | done(); 41 | }); 42 | }); 43 | 44 | it('Params: Bad agent id', function(done) { 45 | request(common.url) 46 | .get("/rootcheck/abc/last_scan") 47 | .auth(common.credentials.user, common.credentials.password) 48 | .expect("Content-type",/json/) 49 | .expect(400) 50 | .end(function(err,res){ 51 | if (err) return done(err); 52 | 53 | res.body.should.have.properties(['error', 'message']); 54 | res.body.error.should.equal(600); 55 | done(); 56 | }); 57 | }); 58 | 59 | it('Errors: No agent', function(done) { 60 | request(common.url) 61 | .get("/rootcheck/9999999/last_scan") 62 | .auth(common.credentials.user, common.credentials.password) 63 | .expect("Content-type",/json/) 64 | .expect(200) 65 | .end(function(err,res){ 66 | if (err) return done(err); 67 | 68 | res.body.should.have.properties(['error', 'message']); 69 | res.body.error.should.equal(1701); 70 | done(); 71 | }); 72 | }); 73 | 74 | }); // GET/rootcheck/:agent_id/last_scan 75 | 76 | describe('DELETE/rootcheck/:agent_id', function() { 77 | 78 | it('Request', function(done) { 79 | request(common.url) 80 | .delete("/rootcheck/000") 81 | .auth(common.credentials.user, common.credentials.password) 82 | .expect("Content-type",/json/) 83 | .expect(200) 84 | .end(function(err,res){ 85 | if (err) return done(err); 86 | 87 | res.body.should.have.properties(['error', 'data']); 88 | 89 | res.body.error.should.equal(0); 90 | res.body.data.should.be.an.string; 91 | done(); 92 | }); 93 | }); 94 | 95 | it('Params: Bad agent id', function(done) { 96 | request(common.url) 97 | .delete("/rootcheck/abc") 98 | .auth(common.credentials.user, common.credentials.password) 99 | .expect("Content-type",/json/) 100 | .expect(400) 101 | .end(function(err,res){ 102 | if (err) return done(err); 103 | 104 | res.body.should.have.properties(['error', 'message']); 105 | res.body.error.should.equal(600); 106 | done(); 107 | }); 108 | }); 109 | 110 | it('Errors: No agent', function(done) { 111 | request(common.url) 112 | .delete("/rootcheck/9999999") 113 | .auth(common.credentials.user, common.credentials.password) 114 | .expect("Content-type",/json/) 115 | .expect(200) 116 | .end(function(err,res){ 117 | if (err) return done(err); 118 | 119 | res.body.should.have.properties(['error', 'message']); 120 | res.body.error.should.equal(1701); 121 | done(); 122 | }); 123 | }); 124 | 125 | }); // DELETE/rootcheck/:agent_id 126 | 127 | describe('DELETE/rootcheck', function() { 128 | 129 | it('Request', function(done) { 130 | request(common.url) 131 | .delete("/rootcheck") 132 | .auth(common.credentials.user, common.credentials.password) 133 | .expect("Content-type",/json/) 134 | .expect(200) 135 | .end(function(err,res){ 136 | if (err) return done(err); 137 | 138 | res.body.should.have.properties(['error', 'data']); 139 | 140 | res.body.error.should.equal(0); 141 | res.body.data.should.be.an.string; 142 | done(); 143 | }); 144 | }); 145 | 146 | }); // DELETE/rootcheck 147 | 148 | describe('PUT/rootcheck/:agent_id', function() { 149 | 150 | it('Request', function(done) { 151 | request(common.url) 152 | .put("/rootcheck/000") 153 | .auth(common.credentials.user, common.credentials.password) 154 | .expect("Content-type",/json/) 155 | .expect(200) 156 | .end(function(err,res){ 157 | if (err) return done(err); 158 | 159 | res.body.should.have.properties(['error', 'data']); 160 | 161 | res.body.error.should.equal(0); 162 | res.body.data.should.be.an.string; 163 | done(); 164 | }); 165 | }); 166 | 167 | it('Params: Bad agent id', function(done) { 168 | request(common.url) 169 | .put("/rootcheck/abc") 170 | .auth(common.credentials.user, common.credentials.password) 171 | .expect("Content-type",/json/) 172 | .expect(400) 173 | .end(function(err,res){ 174 | if (err) return done(err); 175 | 176 | res.body.should.have.properties(['error', 'message']); 177 | res.body.error.should.equal(600); 178 | done(); 179 | }); 180 | }); 181 | 182 | it('Errors: No agent', function(done) { 183 | request(common.url) 184 | .put("/rootcheck/9999999") 185 | .auth(common.credentials.user, common.credentials.password) 186 | .expect("Content-type",/json/) 187 | .expect(200) 188 | .end(function(err,res){ 189 | if (err) return done(err); 190 | 191 | res.body.should.have.properties(['error', 'message']); 192 | res.body.error.should.equal(1701); 193 | done(); 194 | }); 195 | }); 196 | }); // PUT/rootcheck/:agent_id 197 | 198 | describe('PUT/rootcheck', function() { 199 | 200 | it('Request', function(done) { 201 | request(common.url) 202 | .put("/rootcheck") 203 | .auth(common.credentials.user, common.credentials.password) 204 | .expect("Content-type",/json/) 205 | .expect(200) 206 | .end(function(err,res){ 207 | if (err) return done(err); 208 | 209 | res.body.should.have.properties(['error', 'data']); 210 | 211 | res.body.error.should.equal(0); 212 | res.body.data.should.be.an.string; 213 | done(); 214 | }); 215 | }); 216 | 217 | }); // PUT/rootcheck 218 | 219 | }); // Rootcheck 220 | -------------------------------------------------------------------------------- /test/test_summary.js: -------------------------------------------------------------------------------- 1 | /** 2 | * API RESTful for OSSEC 3 | * Copyright (C) 2015-2016 Wazuh, Inc.All rights reserved. 4 | * Wazuh.com 5 | * 6 | * This program is a free software; you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public 8 | * License (version 2) as published by the FSF - Free Software 9 | * Foundation. 10 | */ 11 | 12 | var should = require('should'); 13 | var assert = require('assert'); 14 | var request = require('supertest'); 15 | var common = require('./common.js'); 16 | 17 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; 18 | 19 | describe('Summary', function() { 20 | 21 | describe('GET/summary/agents', function() { 22 | 23 | it('Request', function(done) { 24 | request(common.url) 25 | .get("/summary/agents") 26 | .auth(common.credentials.user, common.credentials.password) 27 | .expect("Content-type",/json/) 28 | .expect(200) 29 | .end(function(err,res){ 30 | if (err) return done(err) 31 | 32 | res.body.should.have.properties(['error', 'data']) 33 | 34 | res.body.error.should.equal(0) 35 | res.body.data.should.have.properties(['nodes', 'groups', 'agent_os', 36 | 'agent_version', 'agent_status', 37 | 'last_registered_agent']) 38 | // nodes 39 | res.body.data.nodes.should.have.properties(['items', 'totalItems']) 40 | res.body.data.nodes.should.be.an.integer 41 | res.body.data.nodes.items[0].should.have.properties(['count', 'node_name']) 42 | res.body.data.nodes.items[0].count.should.be.an.integer 43 | res.body.data.nodes.items[0].node_name.should.be.equal('master') 44 | res.body.data.nodes.items[1].should.have.properties(['count', 'node_name']) 45 | res.body.data.nodes.items[1].count.should.be.an.integer 46 | res.body.data.nodes.items[1].node_name.should.be.equal('worker-1') 47 | res.body.data.nodes.items[2].should.have.properties(['count', 'node_name']) 48 | res.body.data.nodes.items[2].count.should.be.an.integer 49 | res.body.data.nodes.items[2].node_name.should.be.equal('worker-2') 50 | // groups 51 | res.body.data.groups.should.have.properties(['items', 'totalItems']) 52 | res.body.data.groups.should.be.an.integer 53 | res.body.data.groups.items[0].should.have.properties(['count', 'name', 'mergedSum', 'configSum']) 54 | // agent_os 55 | res.body.data.agent_os.should.have.properties(['items', 'totalItems']) 56 | res.body.data.agent_os.should.be.an.integer 57 | res.body.data.agent_os.items[0].should.have.properties(['os', 'count']) 58 | res.body.data.agent_os.items[0].os.should.have.properties(['name', 'platform', 'version']) 59 | res.body.data.agent_os.items[0].count.should.be.an.integer 60 | // agent_version 61 | res.body.data.agent_version.should.have.properties(['items', 'totalItems']) 62 | res.body.data.agent_version.should.be.an.integer 63 | res.body.data.agent_version.items[0].should.have.properties(['count', 'version']) 64 | res.body.data.agent_version.items[0].count.should.be.an.integer 65 | res.body.data.agent_version.items[0].version.should.be.String 66 | // agent_status 67 | res.body.data.agent_status.should.have.properties(['Total', 'Active', 'Disconnected', 'Never connected', 'Pending']) 68 | res.body.data.agent_status['Active'].should.be.above(0) 69 | res.body.data.agent_status['Total'].should.be.above(0) 70 | res.body.data.agent_status['Disconnected'].should.be.an.integer 71 | res.body.data.agent_status['Never connected'].should.be.an.integer 72 | res.body.data.agent_status['Pending'].should.be.an.integer 73 | // last_registered_agent 74 | res.body.data.last_registered_agent.should.have.properties(['os', 'ip', 'node_name', 'name', 'dateAdd', 75 | 'id', 'manager', 'mergedSum', 'lastKeepAlive', 76 | 'group', 'version', 'configSum', 'status', 77 | 'registerIP']) 78 | res.body.data.last_registered_agent.os.should.have.properties(['arch', 'major', 'name', 'platform', 'uname', 'version']) 79 | res.body.data.last_registered_agent.group.should.be.an.Array 80 | 81 | done() 82 | 83 | }); 84 | }); 85 | 86 | }); // GET/agents/full_summary 87 | 88 | }); // Summary 89 | 90 | -------------------------------------------------------------------------------- /test/utils/send_to_wdb.py: -------------------------------------------------------------------------------- 1 | 2 | #!/usr/bin/env python3 3 | # -*- coding: UTF-8 -*- 4 | # 5 | # Copyright (C) 2015-2020, Wazuh Inc. 6 | # Created by Wazuh, Inc. . 7 | # This program is free software; you can redistribute 8 | # it and/or modify it under the terms of GPLv2 9 | 10 | """This module sends a message to WazuhDB socket.""" 11 | 12 | import argparse 13 | import os 14 | import socket 15 | import struct 16 | 17 | WDB_SOCKET_PATH = os.path.join('/', 'var', 'ossec', 'queue', 'db', 'wdb') 18 | 19 | 20 | def get_script_arguments(): 21 | """Get script arguments.""" 22 | parser = argparse.ArgumentParser(usage="usage: %(prog)s [options]", 23 | description="Tool for sending queries to WazuhDB", # noqa: E501 24 | formatter_class=argparse.RawTextHelpFormatter) # noqa: E501 25 | 26 | parser.add_argument('-q', '--query', dest='query', 27 | help='Query to send to WazuhDB', required=True) 28 | 29 | return parser.parse_args() 30 | 31 | 32 | def send_query_to_wdb(query: str): 33 | """Send query to WazuhDB socket. 34 | 35 | :param query: Query to send 36 | """ 37 | # connect to WazuhDB socket 38 | wdb_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 39 | wdb_socket.connect(WDB_SOCKET_PATH) 40 | # construct message 41 | encoded_message = query.encode(encoding='utf-8') 42 | final_message = struct.pack('