Issues (37)

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/Hooks/HookManager.php (1 issue)

Labels
Severity

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
namespace Consolidation\AnnotatedCommand\Hooks;
3
4
use Symfony\Component\Console\Command\Command;
5
use Symfony\Component\Console\Input\InputInterface;
6
use Symfony\Component\Console\Output\OutputInterface;
7
8
use Symfony\Component\Console\ConsoleEvents;
9
use Symfony\Component\Console\Event\ConsoleCommandEvent;
10
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
11
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
12
use Symfony\Component\EventDispatcher\EventDispatcher;
13
14
use Consolidation\AnnotatedCommand\ExitCodeInterface;
15
use Consolidation\AnnotatedCommand\OutputDataInterface;
16
use Consolidation\AnnotatedCommand\AnnotationData;
17
use Consolidation\AnnotatedCommand\CommandData;
18
use Consolidation\AnnotatedCommand\CommandError;
19
use Consolidation\AnnotatedCommand\Hooks\Dispatchers\CommandEventHookDispatcher;
20
21
/**
22
 * Manage named callback hooks
23
 */
24
class HookManager implements EventSubscriberInterface
25
{
26
    protected $hooks = [];
27
    /** var CommandInfo[] */
28
    protected $hookOptions = [];
29
30
    const REPLACE_COMMAND_HOOK = 'replace-command';
31
    const PRE_COMMAND_EVENT = 'pre-command-event';
32
    const COMMAND_EVENT = 'command-event';
33
    const POST_COMMAND_EVENT = 'post-command-event';
34
    const PRE_OPTION_HOOK = 'pre-option';
35
    const OPTION_HOOK = 'option';
36
    const POST_OPTION_HOOK = 'post-option';
37
    const PRE_INITIALIZE = 'pre-init';
38
    const INITIALIZE = 'init';
39
    const POST_INITIALIZE = 'post-init';
40
    const PRE_INTERACT = 'pre-interact';
41
    const INTERACT = 'interact';
42
    const POST_INTERACT = 'post-interact';
43
    const PRE_ARGUMENT_VALIDATOR = 'pre-validate';
44
    const ARGUMENT_VALIDATOR = 'validate';
45
    const POST_ARGUMENT_VALIDATOR = 'post-validate';
46
    const PRE_COMMAND_HOOK = 'pre-command';
47
    const COMMAND_HOOK = 'command';
48
    const POST_COMMAND_HOOK = 'post-command';
49
    const PRE_PROCESS_RESULT = 'pre-process';
50
    const PROCESS_RESULT = 'process';
51
    const POST_PROCESS_RESULT = 'post-process';
52
    const PRE_ALTER_RESULT = 'pre-alter';
53
    const ALTER_RESULT = 'alter';
54
    const POST_ALTER_RESULT = 'post-alter';
55
    const STATUS_DETERMINER = 'status';
56
    const EXTRACT_OUTPUT = 'extract';
57
    const ON_EVENT = 'on-event';
58
59
    public function __construct()
60
    {
61
    }
62
63
    public function getAllHooks()
64
    {
65
        return $this->hooks;
66
    }
67
68
    /**
69
     * Add a hook
70
     *
71
     * @param mixed $callback The callback function to call
72
     * @param string   $hook     The name of the hook to add
73
     * @param string   $name     The name of the command to hook
74
     *   ('*' for all)
75
     */
76
    public function add(callable $callback, $hook, $name = '*')
77
    {
78
        if (empty($name)) {
79
            $name = static::getClassNameFromCallback($callback);
80
        }
81
        $this->hooks[$name][$hook][] = $callback;
82
        return $this;
83
    }
84
85
    public function recordHookOptions($commandInfo, $name)
86
    {
87
        $this->hookOptions[$name][] = $commandInfo;
88
        return $this;
89
    }
90
91
    public static function getNames($command, $callback)
92
    {
93
        return array_filter(
94
            array_merge(
95
                static::getNamesUsingCommands($command),
96
                [static::getClassNameFromCallback($callback)]
97
            )
98
        );
99
    }
100
101
    protected static function getNamesUsingCommands($command)
102
    {
103
        return array_merge(
104
            [$command->getName()],
105
            $command->getAliases()
106
        );
107
    }
108
109
    /**
110
     * If a command hook does not specify any particular command
111
     * name that it should be attached to, then it will be applied
112
     * to every command that is defined in the same class as the hook.
113
     * This is controlled by using the namespace + class name of
114
     * the implementing class of the callback hook.
115
     */
116
    protected static function getClassNameFromCallback($callback)
117
    {
118
        if (!is_array($callback)) {
119
            return '';
120
        }
121
        $reflectionClass = new \ReflectionClass($callback[0]);
122
        return $reflectionClass->getName();
0 ignored issues
show
Consider using $reflectionClass->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
123
    }
124
125
    /**
126
     * Add a replace command hook
127
     *
128
     * @param type ReplaceCommandHookInterface $provider
129
     * @param type string $command_name The name of the command to replace
130
     */
131
    public function addReplaceCommandHook(ReplaceCommandHookInterface $replaceCommandHook, $name)
132
    {
133
        $this->hooks[$name][self::REPLACE_COMMAND_HOOK][] = $replaceCommandHook;
134
        return $this;
135
    }
136
137
    public function addPreCommandEventDispatcher(EventDispatcherInterface $eventDispatcher, $name = '*')
138
    {
139
        $this->hooks[$name][self::PRE_COMMAND_EVENT][] = $eventDispatcher;
140
        return $this;
141
    }
142
143
    public function addCommandEventDispatcher(EventDispatcherInterface $eventDispatcher, $name = '*')
144
    {
145
        $this->hooks[$name][self::COMMAND_EVENT][] = $eventDispatcher;
146
        return $this;
147
    }
148
149
    public function addPostCommandEventDispatcher(EventDispatcherInterface $eventDispatcher, $name = '*')
150
    {
151
        $this->hooks[$name][self::POST_COMMAND_EVENT][] = $eventDispatcher;
152
        return $this;
153
    }
154
155
    public function addCommandEvent(EventSubscriberInterface $eventSubscriber)
156
    {
157
        // Wrap the event subscriber in a dispatcher and add it
158
        $dispatcher = new EventDispatcher();
159
        $dispatcher->addSubscriber($eventSubscriber);
160
        return $this->addCommandEventDispatcher($dispatcher);
161
    }
162
163
    /**
164
     * Add an configuration provider hook
165
     *
166
     * @param type InitializeHookInterface $provider
167
     * @param type $name The name of the command to hook
168
     *   ('*' for all)
169
     */
170
    public function addInitializeHook(InitializeHookInterface $initializeHook, $name = '*')
171
    {
172
        $this->hooks[$name][self::INITIALIZE][] = $initializeHook;
173
        return $this;
174
    }
175
176
    /**
177
     * Add an option hook
178
     *
179
     * @param type ValidatorInterface $validator
180
     * @param type $name The name of the command to hook
181
     *   ('*' for all)
182
     */
183
    public function addOptionHook(OptionHookInterface $interactor, $name = '*')
184
    {
185
        $this->hooks[$name][self::INTERACT][] = $interactor;
186
        return $this;
187
    }
188
189
    /**
190
     * Add an interact hook
191
     *
192
     * @param type ValidatorInterface $validator
193
     * @param type $name The name of the command to hook
194
     *   ('*' for all)
195
     */
196
    public function addInteractor(InteractorInterface $interactor, $name = '*')
197
    {
198
        $this->hooks[$name][self::INTERACT][] = $interactor;
199
        return $this;
200
    }
201
202
    /**
203
     * Add a pre-validator hook
204
     *
205
     * @param type ValidatorInterface $validator
206
     * @param type $name The name of the command to hook
207
     *   ('*' for all)
208
     */
209
    public function addPreValidator(ValidatorInterface $validator, $name = '*')
210
    {
211
        $this->hooks[$name][self::PRE_ARGUMENT_VALIDATOR][] = $validator;
212
        return $this;
213
    }
214
215
    /**
216
     * Add a validator hook
217
     *
218
     * @param type ValidatorInterface $validator
219
     * @param type $name The name of the command to hook
220
     *   ('*' for all)
221
     */
222
    public function addValidator(ValidatorInterface $validator, $name = '*')
223
    {
224
        $this->hooks[$name][self::ARGUMENT_VALIDATOR][] = $validator;
225
        return $this;
226
    }
227
228
    /**
229
     * Add a pre-command hook.  This is the same as a validator hook, except
230
     * that it will run after all of the post-validator hooks.
231
     *
232
     * @param type ValidatorInterface $preCommand
233
     * @param type $name The name of the command to hook
234
     *   ('*' for all)
235
     */
236
    public function addPreCommandHook(ValidatorInterface $preCommand, $name = '*')
237
    {
238
        $this->hooks[$name][self::PRE_COMMAND_HOOK][] = $preCommand;
239
        return $this;
240
    }
241
242
    /**
243
     * Add a post-command hook.  This is the same as a pre-process hook,
244
     * except that it will run before the first pre-process hook.
245
     *
246
     * @param type ProcessResultInterface $postCommand
247
     * @param type $name The name of the command to hook
248
     *   ('*' for all)
249
     */
250
    public function addPostCommandHook(ProcessResultInterface $postCommand, $name = '*')
251
    {
252
        $this->hooks[$name][self::POST_COMMAND_HOOK][] = $postCommand;
253
        return $this;
254
    }
255
256
    /**
257
     * Add a result processor.
258
     *
259
     * @param type ProcessResultInterface $resultProcessor
260
     * @param type $name The name of the command to hook
261
     *   ('*' for all)
262
     */
263
    public function addResultProcessor(ProcessResultInterface $resultProcessor, $name = '*')
264
    {
265
        $this->hooks[$name][self::PROCESS_RESULT][] = $resultProcessor;
266
        return $this;
267
    }
268
269
    /**
270
     * Add a result alterer. After a result is processed
271
     * by a result processor, an alter hook may be used
272
     * to convert the result from one form to another.
273
     *
274
     * @param type AlterResultInterface $resultAlterer
275
     * @param type $name The name of the command to hook
276
     *   ('*' for all)
277
     */
278
    public function addAlterResult(AlterResultInterface $resultAlterer, $name = '*')
279
    {
280
        $this->hooks[$name][self::ALTER_RESULT][] = $resultAlterer;
281
        return $this;
282
    }
283
284
    /**
285
     * Add a status determiner. Usually, a command should return
286
     * an integer on error, or a result object on success (which
287
     * implies a status code of zero). If a result contains the
288
     * status code in some other field, then a status determiner
289
     * can be used to call the appropriate accessor method to
290
     * determine the status code.  This is usually not necessary,
291
     * though; a command that fails may return a CommandError
292
     * object, which contains a status code and a result message
293
     * to display.
294
     * @see CommandError::getExitCode()
295
     *
296
     * @param type StatusDeterminerInterface $statusDeterminer
297
     * @param type $name The name of the command to hook
298
     *   ('*' for all)
299
     */
300
    public function addStatusDeterminer(StatusDeterminerInterface $statusDeterminer, $name = '*')
301
    {
302
        $this->hooks[$name][self::STATUS_DETERMINER][] = $statusDeterminer;
303
        return $this;
304
    }
305
306
    /**
307
     * Add an output extractor. If a command returns an object
308
     * object, by default it is passed directly to the output
309
     * formatter (if in use) for rendering. If the result object
310
     * contains more information than just the data to render, though,
311
     * then an output extractor can be used to call the appopriate
312
     * accessor method of the result object to get the data to
313
     * rendered.  This is usually not necessary, though; it is preferable
314
     * to have complex result objects implement the OutputDataInterface.
315
     * @see OutputDataInterface::getOutputData()
316
     *
317
     * @param type ExtractOutputInterface $outputExtractor
318
     * @param type $name The name of the command to hook
319
     *   ('*' for all)
320
     */
321
    public function addOutputExtractor(ExtractOutputInterface $outputExtractor, $name = '*')
322
    {
323
        $this->hooks[$name][self::EXTRACT_OUTPUT][] = $outputExtractor;
324
        return $this;
325
    }
326
327
    public function getHookOptionsForCommand($command)
328
    {
329
        $names = $this->addWildcardHooksToNames($command->getNames(), $command->getAnnotationData());
330
        return $this->getHookOptions($names);
331
    }
332
333
    /**
334
     * @return CommandInfo[]
335
     */
336
    public function getHookOptions($names)
337
    {
338
        $result = [];
339
        foreach ($names as $name) {
340
            if (isset($this->hookOptions[$name])) {
341
                $result = array_merge($result, $this->hookOptions[$name]);
342
            }
343
        }
344
        return $result;
345
    }
346
347
    /**
348
     * Get a set of hooks with the provided name(s). Include the
349
     * pre- and post- hooks, and also include the global hooks ('*')
350
     * in addition to the named hooks provided.
351
     *
352
     * @param string|array $names The name of the function being hooked.
353
     * @param string[] $hooks A list of hooks (e.g. [HookManager::ALTER_RESULT])
354
     *
355
     * @return callable[]
356
     */
357
    public function getHooks($names, $hooks, $annotationData = null)
358
    {
359
        return $this->get($this->addWildcardHooksToNames($names, $annotationData), $hooks);
360
    }
361
362
    protected function addWildcardHooksToNames($names, $annotationData = null)
363
    {
364
        $names = array_merge(
365
            (array)$names,
366
            ($annotationData == null) ? [] : array_map(function ($item) {
367
                return "@$item";
368
            }, $annotationData->keys())
369
        );
370
        $names[] = '*';
371
        return array_unique($names);
372
    }
373
374
    /**
375
     * Get a set of hooks with the provided name(s).
376
     *
377
     * @param string|array $names The name of the function being hooked.
378
     * @param string[] $hooks The list of hook names (e.g. [HookManager::ALTER_RESULT])
379
     *
380
     * @return callable[]
381
     */
382
    public function get($names, $hooks)
383
    {
384
        $result = [];
385
        foreach ((array)$hooks as $hook) {
386
            foreach ((array)$names as $name) {
387
                $result = array_merge($result, $this->getHook($name, $hook));
388
            }
389
        }
390
        return $result;
391
    }
392
393
    /**
394
     * Get a single named hook.
395
     *
396
     * @param string $name The name of the hooked method
397
     * @param string $hook The specific hook name (e.g. alter)
398
     *
399
     * @return callable[]
400
     */
401
    public function getHook($name, $hook)
402
    {
403
        if (isset($this->hooks[$name][$hook])) {
404
            return $this->hooks[$name][$hook];
405
        }
406
        return [];
407
    }
408
409
    /**
410
     * Call the command event hooks.
411
     *
412
     * TODO: This should be moved to CommandEventHookDispatcher, which
413
     * should become the class that implements EventSubscriberInterface.
414
     * This change would break all clients, though, so postpone until next
415
     * major release.
416
     *
417
     * @param ConsoleCommandEvent $event
418
     */
419
    public function callCommandEventHooks(ConsoleCommandEvent $event)
420
    {
421
        /* @var Command $command */
422
        $command = $event->getCommand();
423
        $dispatcher = new CommandEventHookDispatcher($this, [$command->getName()]);
424
        $dispatcher->callCommandEventHooks($event);
425
    }
426
427
    /**
428
     * @{@inheritdoc}
429
     */
430
    public static function getSubscribedEvents()
431
    {
432
        return [ConsoleEvents::COMMAND => 'callCommandEventHooks'];
433
    }
434
}
435