This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
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
|
|||
116 | } |
||
117 | } |
||
118 | else { |
||
119 | $this->monolog->addCritical(sprintf("'%s' command is not supported.", $cmd)); |
||
120 | exit(Server::EXIT_FAILURE); |
||
0 ignored issues
–
show
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 ![]() |
|||
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. ![]() |
|||
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, ![]() |
|||
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 | } |
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.