Issues (11)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/EoC/Server.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * @file Server.php
5
 * @brief This file contains the Server class.
6
 * @details
7
 * @author Filippo F. Fadda
8
 */
9
10
11
namespace EoC;
12
13
14
use EoC\Command;
15
use EoC\Handler\CouchHandler;
16
17
use Monolog\Logger;
18
use Monolog\ErrorHandler;
19
use Monolog\Handler\StreamHandler;
20
21
22
/**
23
 * @brief ElephantOnCouch query server main class.
24
 * @warning This class won't work with CGI because uses standard input (STDIN) and standard output (STDOUT).
25
 * @see http://wiki.apache.org/couchdb/View_server
26
 */
27
final class Server {
28
  const EXIT_SUCCESS = 0;
29
  const EXIT_FAILURE = 1;
30
31
  private $monolog; // Stores the logger instance.
32
33
  private $commands = []; // Stores the commands' list.
34
35
  private $funcs; // Stores the functions' list.
36
37
  private $reduceLimit; // Not used.
38
  private $timeout; // Not used.
39
40
41
  /**
42
   * @brief Creates a Server instance.
43
   * @details To enable debug logging you must pass the log file name to the constructor, otherwise high level info and
44
   * errors are saved on the `couch.log` file.
45
   * @param[in] string $fileName The complete log file path.
46
   */
47
  public function __construct($fileName = "") {
48
    // Creates a Monolog instance.
49
    $this->monolog = new Logger('eocsvr');
50
51
    // Registers the Monolog error handler to log errors and exceptions.
52
    ErrorHandler::register($this->monolog);
53
54
    // Using this handler we are able to log messages and errors to couch.log file.
55
    $this->monolog->pushHandler(new CouchHandler($this));
56
57
    // If a log file is provided, creates a stream handler to log debugging messages.
58
    if (!empty($fileName))
59
      $this->monolog->pushHandler(new StreamHandler($fileName, Logger::DEBUG));
60
61
    // Get all available commands.
62
    $this->loadCommands();
63
64
    $this->funcs = [];
65
  }
66
67
68
  /**
69
   * @brief Destroy the Server instance previously created.
70
   */
71
  public function __destruct() {
72
  }
73
74
75
  /**
76
   * @brief Initializes the commands list.
77
   * @details CouchDB communicates with a Query Server over standard input/output. Each line represents a command.
78
   * Every single command must be interpreted and executed by a specific command handler.
79
   */
80
  private function loadCommands() {
81
    $this->commands[Command\AddFunCmd::getName()] = Command\AddFunCmd::getClass();
82
    $this->commands[Command\MapDocCmd::getName()] = Command\MapDocCmd::getClass();
83
    $this->commands[Command\ReduceCmd::getName()] = Command\ReduceCmd::getClass();
84
    $this->commands[Command\RereduceCmd::getName()] = Command\RereduceCmd::getClass();
85
    $this->commands[Command\ResetCmd::getName()] = Command\ResetCmd::getClass();
86
  }
87
88
89
  /**
90
   * @brief Starts the server.
91
   */
92
  public function run() {
93
    $this->monolog->addDebug("RUN");
94
95
    while ($line = trim(fgets(STDIN))) {
96
      // We decode the JSON string into an array. Returned objects will be converted into associative arrays.
97
      $args = json_decode($line, TRUE);
98
99
      // We know that the first part of the JSON encoded string represent the command.
100
      // Only the command implementation knows which and how many arguments are provided for the command itself.
101
      $cmd = array_shift($args);
102
103
      $this->monolog->addDebug("Line: ".$line);
104
      $this->monolog->addDebug("Command Name: ".$cmd);
105
      $this->monolog->addDebug("Args Type: ".gettype($args));
106
107
      if (array_key_exists($cmd, $this->commands)) {
108
        try {
109
          $class = $this->commands[$cmd];
110
          $cmdObj = new $class($this, $args);
111
          $cmdObj->execute();
112
        }
113
        catch (\Exception $e) {
114
          $this->monolog->addCritical($e->getMessage());
115
          exit(Server::EXIT_FAILURE);
0 ignored issues
show
Coding Style Compatibility introduced by
The method run() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
116
        }
117
      }
118
      else {
119
        $this->monolog->addCritical(sprintf("'%s' command is not supported.", $cmd));
120
        exit(Server::EXIT_FAILURE);
0 ignored issues
show
Coding Style Compatibility introduced by
The method run() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
121
      }
122
123
    }
124
  }
125
126
127
  /**
128
   * @brief Sends a response to CouchDB via standard output.
129
   * @param[in] string $str The string to send.
130
   */
131
  public function writeln($str) {
132
    // CouchDB's message terminator is: \n.
133
    fputs(STDOUT, $str."\n");
134
    flush();
135
  }
136
137
138
  /**
139
   * @brief Resets the array of the functions.
140
   */
141
  public function resetFuncs() {
142
    unset($this->funcs);
143
    $this->funcs = [];
144
  }
145
146
147
  /**
148
   * @brief Returns the array of the functions.
149
   */
150
  public function getFuncs() {
151
    return $this->funcs;
152
  }
153
154
155
  /**
156
   * @brief Add the given function to the internal functions' list.
157
   * @param[in] string $fn The function implementation.
158
   */
159
  public function addFunc($fn) {
160
    $this->funcs[] = $fn;
161
  }
162
163
164
  /**
165
   * @brief The Map step generates a set of key/valu pairs which can then optionally be reduced to a single value - or
166
   * to a grouping of values - in the Reduce step.
167
   * @details If a view has a reduce function, it is used to produce aggregate results for that view. A reduce function
168
   * is passed a set of intermediate values and combines them to a single value. Reduce functions must accept, as input,
169
   * results emitted by its corresponding map function as well as results returned by the reduce function itself. The
170
   * latter case is referred to as a rereduce.\n
171
   * This function is called by commands ReduceCmd and RereduceCmd.
172
   * @param[in] array $funcs An array of reduce functions.
173
   * @param[in] array $keys An array of mapped keys and document IDs in the form of [key, id].
174
   * @param[in] array $values An array of mapped values.
175
   * @param[in] bool $rereduce When `true` the values will be reduced again.
176
   * @warning This function ignores the value of `reduce_limit`, because the author thinks the algorithm used by
177
   * the JavaScript query server sucks.
178
   */
179
  public function reduce($funcs, $keys, $values, $rereduce) {
180
    $closure = NULL; // This initialization is made just to prevent a lint error during development.
181
182
    $reductions = [];
183
184
    // Executes the reductions.
185 View Code Duplication
    foreach ($funcs as $fn) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
186
      $this->monolog->addDebug($fn);
187
188
      eval("\$closure = ".$fn);
0 ignored issues
show
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
189
190
      if (is_callable($closure))
191
        $reductions[] = call_user_func($closure, $keys, $values, $rereduce);
192
      else
193
        throw new \BadFunctionCallException("The reduce function is not callable.");
194
    }
195
196
    // Sends mappings to CouchDB.
197
    $this->writeln("[true,".json_encode($reductions)."]");
198
  }
199
200
201
  /**
202
   * @brief Tells CouchDB to append the specified message in the couch.log file.
203
   * @details Any message will appear in the couch.log file, as follows:
204
   @code
205
     [Tue, 22 May 2012 15:26:03 GMT] [info] [<0.80.0>] This is a log message
206
   @endcode
207
   * You can't force the message's level. Every message will be marked as [info] even in case of an error, because
208
   * CouchDB doesn't let you specify a different level. In case or error use error(), forbidden() or unauthorized()
209
   * instead.
210
   * @warning Keep in mind that you can't use this method inside reset() or addFun(), because you are going to
211
   * generate an error. CouchDB, in fact, does not expect a message when it sends `reset` or `add_fun` commands.
212
   * @param[in] string $msg The message to log.
213
   */
214
  public function log($msg) {
215
    $this->writeln(json_encode(["log", $msg]));
216
  }
217
218
219
  /**
220
   * @brief In case of error CouchDB doesn't take any action. We simply notify the error, sending a special message to it.
221
   * @param[in] string $keyword The error keyword.
222
   * @param[in] string $reason The error message.
223
   */
224
  public function error($keyword, $reason) {
225
    $this->writeln(json_encode(["error", $keyword, $reason]));
226
  }
227
228
229
  /**
230
   * @brief The forbidden error are widely used by validate document update functions to stop further function processing
231
   * and prevent on disk store of the new document version.
232
   * @details Since this errors actually is not an error, but an assertion against user actions, CouchDB doesn't log it
233
   * at “error” level, but returns HTTP 403 Forbidden response with error information object.
234
   * @param[in] string $reason The error message.
235
   */
236
  public function forbidden($reason) {
237
    $this->writeln(json_encode(["forbidden" => $reason]));
238
  }
239
240
241
  /**
242
   * @brief The unauthorized error mostly acts like forbidden one, but with semantic as please authorize first.
243
   * @details CouchDB doesn't log it at “error” level, but returns HTTP 401 Unauthorized response with error information
244
   * object.
245
   * @param[in] string $reason The error message.
246
   */
247
  public function unauthorized($reason) {
248
    $this->writeln(json_encode(["unauthorized" => $reason]));
249
  }
250
251
252
  /**
253
   * @brief Gets the logger instance.
254
   */
255
  public function getMonolog() {
256
    return $this->monolog;
257
  }
258
259
260
  /**
261
   * @brief Sets the limit of times a reduce function can be called.
262
   * @warning Actually `reduce_limit` config option is a boolean.
263
   */
264
  public function setReduceLimit($value) {
265
    $this->reduceLimit = $value;
266
  }
267
268
269
  /**
270
   * @brief Sets the timeout for the reduce process.
271
   */
272
  public function setTimeout($value) {
273
    $this->timeout = (integer)$value;
274
  }
275
276
}