Issues (90)

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/delivery/cli/CliApplication.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
namespace rtens\domin\delivery\cli;
3
4
use rtens\domin\Action;
5
use rtens\domin\ActionRegistry;
6
use rtens\domin\delivery\cli\fields\ArrayField;
7
use rtens\domin\delivery\cli\fields\BooleanField;
8
use rtens\domin\delivery\cli\fields\DateIntervalField;
9
use rtens\domin\delivery\cli\fields\DateTimeField;
10
use rtens\domin\delivery\cli\fields\EnumerationField;
11
use rtens\domin\delivery\cli\fields\FileField;
12
use rtens\domin\delivery\cli\fields\HtmlField;
13
use rtens\domin\delivery\cli\fields\IdentifierField;
14
use rtens\domin\delivery\cli\fields\MultiField;
15
use rtens\domin\delivery\cli\fields\NullableField;
16
use rtens\domin\delivery\cli\fields\ObjectField;
17
use rtens\domin\delivery\cli\fields\PrimitiveField;
18
use rtens\domin\delivery\cli\fields\RangeField;
19
use rtens\domin\delivery\cli\renderers\ArrayRenderer;
20
use rtens\domin\delivery\cli\renderers\BooleanRenderer;
21
use rtens\domin\delivery\cli\renderers\ChartRenderer;
22
use rtens\domin\delivery\cli\renderers\DateIntervalRenderer;
23
use rtens\domin\delivery\cli\renderers\DateTimeRenderer;
24
use rtens\domin\delivery\cli\renderers\DelayedOutputRenderer;
25
use rtens\domin\delivery\cli\renderers\FileRenderer;
26
use rtens\domin\delivery\cli\renderers\HtmlRenderer;
27
use rtens\domin\delivery\cli\renderers\IdentifierRenderer;
28
use rtens\domin\delivery\cli\renderers\ObjectRenderer;
29
use rtens\domin\delivery\cli\renderers\PrimitiveRenderer;
30
use rtens\domin\delivery\cli\renderers\tables\DataTableRenderer;
31
use rtens\domin\delivery\cli\renderers\tables\ObjectTableRenderer;
32
use rtens\domin\delivery\cli\renderers\tables\TableRenderer;
33
use rtens\domin\delivery\FieldRegistry;
34
use rtens\domin\delivery\ParameterReader;
35
use rtens\domin\delivery\RendererRegistry;
36
use rtens\domin\execution\access\AccessControl;
37
use rtens\domin\execution\ExecutionResult;
38
use rtens\domin\execution\FailedResult;
39
use rtens\domin\execution\MissingParametersResult;
40
use rtens\domin\execution\NoResult;
41
use rtens\domin\execution\NotPermittedResult;
42
use rtens\domin\execution\RedirectResult;
43
use rtens\domin\execution\ValueResult;
44
use rtens\domin\Executor;
45
use rtens\domin\reflection\CommentParser;
46
use rtens\domin\reflection\types\TypeFactory;
47
use watoki\factory\Factory;
48
49
class CliApplication {
50
51
    const INTERACTIVE_MODE = '!';
52
53
    const OK = 0;
54
    const ERROR = 1;
55
56
    /** @var Factory */
57
    public $factory;
58
59
    /** @var ActionRegistry */
60
    public $actions;
61
62
    /** @var FieldRegistry */
63
    public $fields;
64
65
    /** @var RendererRegistry */
66
    public $renderers;
67
68
    /** @var TypeFactory */
69
    public $types;
70
71
    /** @var CommentParser */
72
    public $parser;
73
74
    /** @var AccessControl */
75
    public $access;
76
77
    /**
78
     * @param Factory $factory <-
79
     * @param ActionRegistry $actions <-
80
     * @param FieldRegistry $fields <-
81
     * @param RendererRegistry $renderers <-
82
     * @param TypeFactory $types <-
83
     * @param CommentParser $parser <-
84
     * @param AccessControl $access <-
85
     */
86
    public function __construct(Factory $factory, ActionRegistry $actions, FieldRegistry $fields,
87
                                RendererRegistry $renderers, TypeFactory $types, CommentParser $parser,
88
                                AccessControl $access) {
89
        $this->factory = $factory;
90
91
        $this->actions = $actions;
92
        $this->fields = $fields;
93
        $this->renderers = $renderers;
94
        $this->types = $types;
95
        $this->parser = $parser;
96
        $this->access = $access;
97
    }
98
99
    /**
100
     * @param callable $callback Receives the CliApplication instance
101
     * @param null|Factory $factory
102
     * @return Factory
103
     */
104
    public static function init(callable $callback, Factory $factory = null) {
105
        $factory = $factory ?: new Factory();
106
        $callback($factory->setSingleton($factory->getInstance(self::class)));
107
        return $factory;
108
    }
109
110
    public static function run(Factory $factory, Console $console = null) {
111
        global $argv;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
112
113
        /** @var self $app */
114
        $app = $factory->getInstance(self::class);
115
        return $app->doRun($console ?: new Console($argv));
116
    }
117
118
    private function doRun(Console $console) {
119
        $outFile = null;
120
121
        if ($console->getArguments()) {
122
            if ($console->getArguments()[0] == self::INTERACTIVE_MODE) {
123
                $outFile = $console->getOption('out', null);
124
125
                $actionId = $this->selectAction($console);
126
                $reader = new InteractiveCliParameterReader($this->fields, $console);
127
128
                $this->printActionHeader($console, $actionId);
129
            } else {
130
                $actionId = $console->getArguments()[0];
131
                $reader = new CliParameterReader($console);
132
            }
133
        } else {
134
            $this->printUsage($console);
135
            $this->printActions($console);
136
            return self::OK;
137
        }
138
139
        $this->registerFields($reader);
140
        $this->registerRenderers();
141
142
        $executor = new Executor($this->actions, $this->fields, $reader, $this->access);
143
        return $this->printResult($console, $executor->execute($actionId), $outFile);
144
    }
145
146
    private function printResult(Console $console, ExecutionResult $result, $outFile) {
147
        if ($result instanceof ValueResult) {
148
            $value = $result->getValue();
149
            $rendered = (string)$this->renderers->getRenderer($value)->render($value);
150
151
            if ($outFile) {
152
                file_put_contents($outFile, $rendered);
153
                $console->writeLine("Output written to [$outFile]");
154
            } else {
155
                $console->writeLine($rendered);
156
            }
157
            return self::OK;
158
159
        } else if ($result instanceof MissingParametersResult) {
160
            $console->writeLine();
161
            $console->writeLine("Missing parameters!");
162
            foreach ($result->getMissingNames() as $missing) {
163
                $console->writeLine('  ' . $missing . ': ' . $result->getException($missing)->getMessage());
164
            }
165
            return self::ERROR;
166
167
        } else if ($result instanceof NotPermittedResult) {
168
            $console->writeLine('Permission denied');
169
            return self::ERROR;
170
171
        } else if ($result instanceof FailedResult) {
172
            $console->writeLine("Error: " . $result->getMessage());
173
174
            $exception = $result->getException();
175
            $console->error(
176
                get_class($exception) . ': ' . $exception->getMessage() . ' ' .
177
                '[' . $exception->getFile() . ':' . $exception->getLine() . ']' . "\n" .
178
                $exception->getTraceAsString()
179
            );
180
            return $exception->getCode() ?: self::ERROR;
181
182
        } else if ($result instanceof NoResult || $result instanceof RedirectResult) {
183
            return self::OK;
184
185
        } else {
186
            $console->writeLine('Cannot print [' . (new \ReflectionClass($result))->getShortName() . ']');
187
            return self::OK;
188
        }
189
    }
190
191
    private function selectAction(Console $console) {
192
        $console->writeLine();
193
        $console->writeLine('Available Actions');
194
        $console->writeLine('~~~~~~~~~~~~~~~~~');
195
196
        $i = 1;
197
        $actionIds = [];
198 View Code Duplication
        foreach ($this->actions->getAllActions() as $id => $action) {
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...
199
            $console->writeLine($i++ . " - " . $action->caption() . $this->shortDescription($action));
200
            $actionIds[] = $id;
201
        }
202
203
        $console->writeLine();
204
        $actionIndex = $console->read('Action: ');
205
206
        return $actionIds[$actionIndex - 1];
207
    }
208
209
    private function printActionHeader(Console $console, $actionId) {
210
        $action = $this->actions->getAction($actionId);
211
        $console->writeLine();
212
        $console->writeLine($action->caption());
213
        $console->writeLine(str_repeat('~', strlen($action->caption())));
214
        $console->writeLine();
215
216
        if ($action->description()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $action->description() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
217
            $console->writeLine($action->description());
218
            $console->writeLine();
219
        }
220
    }
221
222
    private function printUsage(Console $console) {
223
        $console->writeLine();
224
225
        $console->writeLine("Interactive mode: php {$console->getScriptName()} !");
226
        $console->writeLine("Execute Action:   php {$console->getScriptName()} <actionId> --<parameterName> <parameterValue> ...");
227
        $console->writeLine();
228
    }
229
230
    private function printActions(Console $console) {
231
        $console->writeLine('Available Actions');
232
        $console->writeLine('~~~~~~~~~~~~~~~~~');
233
234 View Code Duplication
        foreach ($this->actions->getAllActions() as $id => $action) {
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...
235
            $console->writeLine($id . ' - ' . $action->caption() . $this->shortDescription($action));
236
        }
237
        $console->writeLine();
238
    }
239
240
    private function registerFields(ParameterReader $reader) {
241
        $this->fields->add(new PrimitiveField());
242
        $this->fields->add(new RangeField());
243
        $this->fields->add(new BooleanField());
244
        $this->fields->add(new FileField());
245
        $this->fields->add(new HtmlField($reader));
246
        $this->fields->add(new DateTimeField());
247
        $this->fields->add(new DateIntervalField());
248
        $this->fields->add(new ArrayField($this->fields, $reader));
249
        $this->fields->add(new NullableField($this->fields, $reader));
250
        $this->fields->add(new ObjectField($this->types, $this->fields, $reader));
251
        $this->fields->add(new MultiField($this->fields, $reader));
252
        $this->fields->add(new IdentifierField($this->fields));
253
        $this->fields->add(new EnumerationField($this->fields));
254
    }
255
256
    private function registerRenderers() {
257
        $this->renderers->add(new BooleanRenderer());
258
        $this->renderers->add(new PrimitiveRenderer());
259
        $this->renderers->add(new DateTimeRenderer());
260
        $this->renderers->add(new DateIntervalRenderer());
261
        $this->renderers->add(new HtmlRenderer());
262
        $this->renderers->add(new IdentifierRenderer());
263
        $this->renderers->add(new FileRenderer(''));
264
        $this->renderers->add(new DelayedOutputRenderer());
265
        $this->renderers->add(new ObjectTableRenderer($this->renderers));
266
        $this->renderers->add(new DataTableRenderer($this->renderers));
267
        $this->renderers->add(new TableRenderer($this->renderers));
268
        $this->renderers->add(new ChartRenderer($this->renderers));
269
        $this->renderers->add(new ArrayRenderer($this->renderers));
270
        $this->renderers->add(new ObjectRenderer($this->renderers, $this->types));
271
    }
272
273
    private function shortDescription(Action $action) {
274
        $description = $this->parser->shorten($action->description());
275
        return $description ? " ($description)" : '';
276
    }
277
}
278