Issues (6)

Security Analysis    not enabled

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/Commands/ResourceMakeCommand.php (1 issue)

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
namespace DrawMyAttention\ResourceGenerator\Commands;
4
5
use Illuminate\Support\Str;
6
use Illuminate\Console\Command;
7
use Illuminate\Support\Composer;
8
use Illuminate\Filesystem\Filesystem;
9
10
class ResourceMakeCommand extends Command
11
{
12
    /**
13
     * The name and signature of the console command.
14
     *
15
     * @var string
16
     */
17
    protected $signature = 'make:resource {name : The model name} {attributes?}';
18
19
    /**
20
     * The console command description.
21
     *
22
     * @var string
23
     */
24
    protected $description = 'Create a new model, migration, controller and add routes';
25
26
    /**
27
     * The filesystem instance.
28
     *
29
     * @var \Illuminate\Filesystem\Filesystem
30
     */
31
    private $files;
32
33
    /**
34
     * @var Composer
35
     */
36
    private $composer;
37
38
    /**
39
     * @var array The data types that can be created in a migration.
40
     */
41
    private $dataTypes = [
42
        'string', 'integer', 'boolean', 'bigIncrements', 'bigInteger',
43
        'binary', 'boolean', 'char', 'date', 'dateTime', 'float', 'increments',
44
        'json', 'jsonb', 'longText', 'mediumInteger', 'mediumText', 'nullableTimestamps',
45
        'smallInteger', 'tinyInteger', 'softDeletes', 'text', 'time', 'timestamp',
46
        'timestamps', 'rememberToken',
47
    ];
48
49
    private $fakerMethods = [
50
        'string' => ['method' => 'words', 'parameters' => '2, true'],
51
        'integer' => ['method' => 'randomNumber', 'parameters' => ''],
52
    ];
53
54
    /**
55
     * @var array $columnProperties Properties that can be applied to a table column.
56
     */
57
    private $columnProperties = [
58
        'unsigned', 'index', 'nullable'
59
    ];
60
61
    /**
62
     * Create a new command instance.
63
     *
64
     * @param Filesystem $files
65
     * @param Composer $composer
66
     */
67
    public function __construct(Filesystem $files, Composer $composer)
68
    {
69
        parent::__construct();
70
71
        $this->files = $files;
72
73
        $this->composer = $composer;
74
    }
75
76
    /**
77
     * Execute the console command.
78
     *
79
     * @return mixed
80
     */
81
    public function handle()
82
    {
83
        $name = trim($this->input->getArgument('name'));
84
85
        $this->createModel($name);
86
87
        $this->createMigration($name);
88
89
        $this->createController($name);
90
91
        $this->appendRoutes($name);
92
93
        $this->createModelFactory($name);
94
    }
95
96
    private function createModelFactory($name)
97
    {
98
        $model = $this->modelName($name);
99
100
        $stub = $this->files->get(__DIR__ . '/../Stubs/factory.stub');
101
102
        $stub = str_replace('CLASSNAME', $model, $stub);
103
104
        $class = 'App\\' . $model;
105
        $model = new $class;
106
107
        $stub = str_replace(
108
            'ATTRIBUTES',
109
            $this->buildFakerAttributes($model->migrationAttributes()),
110
            $stub
111
        );
112
113
        $this->files->append(database_path('factories/ModelFactory.php'), $stub);
114
115
        $this->info('Created model factory');
116
117
        return true;
118
    }
119
120
    public function buildFakerAttributes($attributes)
121
    {
122
        $faker = '';
123
124
        foreach ($attributes as $attribute) {
125
126
            $formatter =
127
                $this->fakerMethods[$this->getFieldTypeFromProperties($attribute['properties'])];
128
129
            $method = $formatter['method'];
130
            $parameters = $formatter['parameters'];
131
132
            $faker .= "'".$attribute['name']."' => \$faker->".$method."(".$parameters.")," . PHP_EOL . '        ';
133
134
        }
135
136
        return rtrim($faker);
137
    }
138
139
    /**
140
     * Create and store a new Model to the filesystem.
141
     *
142
     * @param string $name
143
     * @return bool
144
     */
145
    private function createModel($name)
146
    {
147
        $modelName = $this->modelName($name);
148
149
        $filename = $modelName . '.php';
150
151
        if ($this->files->exists(app_path($filename))) {
152
            $this->error('Model already exists!');
153
            return false;
154
        }
155
156
        $model = $this->buildModel($name);
157
158
        $this->files->put(app_path('/' . $filename), $model);
159
160
        $this->info($modelName . ' Model created');
161
162
        return true;
163
    }
164
165
    private function createMigration($name)
166
    {
167
        $filename = $this->buildMigrationFilename($name);
168
169
        if ($this->files->exists(database_path($filename))) {
170
            $this->error('Migration already exists!');
171
            return false;
172
        }
173
174
        $migration = $this->buildMigration($name);
175
176
        $this->files->put(
177
            database_path('/migrations/' . $filename),
178
            $migration
179
        );
180
181
        if (env('APP_ENV') != 'testing') {
182
            $this->composer->dumpAutoloads();
183
        }
184
185
        $this->info('Created migration ' . $filename);
186
187
        return true;
188
    }
189
190
    private function createController($modelName)
191
    {
192
        $filename = ucfirst($modelName) . 'Controller.php';
193
194
        if ($this->files->exists(app_path('Http/' . $filename))) {
195
            $this->error('Controller already exists!');
196
            return false;
197
        }
198
199
        $stub = $this->files->get(__DIR__ . '/../Stubs/controller.stub');
200
201
        $stub = str_replace('MyModelClass', ucfirst($modelName), $stub);
202
        $stub = str_replace('myModelInstance', Str::camel($modelName), $stub);
203
        $stub = str_replace('template', strtolower($modelName), $stub);
204
205
        $this->files->put(app_path('Http/Controllers/' . $filename), $stub);
206
207
        $this->info('Created controller ' . $filename);
208
209
        return true;
210
    }
211
212
    private function appendRoutes($modelName)
213
    {
214
        $modelTitle = ucfirst($modelName);
215
216
        $modelName = strtolower($modelName);
217
218
        $newRoutes = $this->files->get(__DIR__ . '/../Stubs/routes.stub');
219
220
        $newRoutes = str_replace('|MODEL_TITLE|', $modelTitle, $newRoutes);
221
222
        $newRoutes = str_replace('|MODEL_NAME|', $modelName, $newRoutes);
223
224
        $newRoutes = str_replace('|CONTROLLER_NAME|', $modelTitle . 'Controller', $newRoutes);
225
226
        $this->files->append(
227
            app_path('Http/routes.php'),
228
            $newRoutes
229
        );
230
231
        $this->info('Added routes for ' . $modelTitle);
232
    }
233
234
    protected function buildMigration($name)
235
    {
236
        $stub = $this->files->get(__DIR__ . '/../Stubs/migration.stub');
237
238
        $className = 'Create' . Str::plural($name). 'Table';
239
240
        $stub = str_replace('MIGRATION_CLASS_PLACEHOLDER', $className, $stub);
241
242
        $table = strtolower(Str::plural($name));
243
244
        $stub = str_replace('TABLE_NAME_PLACEHOLDER', $table, $stub);
245
246
        $class = 'App\\' . $name;
247
        $model = new $class;
248
249
        $stub = str_replace('MIGRATION_COLUMNS_PLACEHOLDER', $this->buildTableColumns($model->migrationAttributes()), $stub);
250
251
        return $stub;
252
    }
253
254
    protected function buildModel($name)
255
    {
256
        $stub = $this->files->get(__DIR__ . '/../Stubs/model.stub');
257
258
        $stub = $this->replaceClassName($name, $stub);
259
260
        $stub = $this->addMigrationAttributes($this->argument('attributes'), $stub);
261
262
        $stub = $this->addModelAttributes('fillable', $this->argument('attributes'), $stub);
263
264
        $stub = $this->addModelAttributes('hidden', $this->argument('attributes'), $stub);
265
266
        return $stub;
267
    }
268
269
    public function convertModelToTableName($model)
270
    {
271
        return Str::plural(Str::snake($model));
272
    }
273
274
    public function buildMigrationFilename($model)
275
    {
276
        $table = $this->convertModelToTableName($model);
277
278
        return date('Y_m_d_his') . '_create_' . $table . '_table.php';
279
    }
280
281
    private function replaceClassName($name, $stub)
282
    {
283
        return str_replace('NAME_PLACEHOLDER', $name, $stub);
284
    }
285
286
    private function addMigrationAttributes($text, $stub)
287
    {
288
        $attributesAsArray = $this->parseAttributesFromInputString($text);
289
        $attributesAsText = $this->convertArrayToString($attributesAsArray);
290
291
        return str_replace('MIGRATION_ATTRIBUTES_PLACEHOLDER', $attributesAsText, $stub);
292
    }
293
294
    /**
295
     * Convert a pipe-separated list of attributes to an array.
296
     *
297
     * @param string $text The Pipe separated attributes
298
     * @return array
299
     */
300
    public function parseAttributesFromInputString($text)
301
    {
302
        $parts = explode('|', $text);
303
304
        $attributes = [];
305
306
        foreach ($parts as $part) {
307
            $components = explode(':', $part);
308
            $attributes[$components[0]] =
309
                isset($components[1]) ? explode(',', $components[1]) : [];
310
        }
311
312
        return $attributes;
313
314
    }
315
316
    /**
317
     * Convert a PHP array into a string version.
318
     *
319
     * @param $array
320
     *
321
     * @return string
322
     */
323
    public function convertArrayToString($array)
324
    {
325
        $string = '[';
326
327
        foreach ($array as $name => $properties) {
328
            $string .= '[';
329
            $string .= "'name' => '" . $name . "',";
330
331
            $string .= "'properties' => [";
332
            foreach ($properties as $property) {
333
                $string .= "'".$property."', ";
334
            }
335
            $string = rtrim($string, ', ');
336
            $string .= ']';
337
338
            $string .= '],';
339
        }
340
341
        $string = rtrim($string, ',');
342
343
        $string .= ']';
344
345
346
        return $string;
347
    }
348
349
    public function addModelAttributes($name, $attributes, $stub)
350
    {
351
        $attributes = '[' . collect($this->parseAttributesFromInputString($attributes))
352
            ->filter(function($attribute) use ($name) {
353
                return in_array($name, $attribute);
354
            })->map(function ($attributes, $name) {
355
                return "'" . $name . "'";
356
            })->values()->implode(', ') . ']';
357
358
359
        return str_replace(strtoupper($name) . '_PLACEHOLDER', $attributes, $stub);
360
    }
361
362
    public function buildTableColumns($attributes)
363
    {
364
365
        return rtrim(collect($attributes)->reduce(function($column, $attribute) {
366
367
            $fieldType = $this->getFieldTypeFromProperties($attribute['properties']);
368
369
            if ($length = $this->typeCanDefineSize($fieldType)) {
370
                $length = $this->extractFieldLengthValue($attribute['properties']);
371
            }
372
373
            $properties = $this->extractAttributePropertiesToApply($attribute['properties']);
374
375
            return $column . $this->buildSchemaColumn($fieldType, $attribute['name'], $length, $properties);
376
377
        }));
378
379
    }
380
381
    /**
382
     * Get the column field type based from the properties of the field being created.
383
     *
384
     * @param array $properties
385
     * @return string
386
     */
387
    private function getFieldTypeFromProperties($properties)
388
    {
389
        $type = array_intersect($properties, $this->dataTypes);
390
391
        // If the properties that were given in the command
392
        // do not explicitly define a data type, or there
393
        // is no matching data type found, the column
394
        // should be cast to a string.
395
396
        if (! $type) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $type of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
397
            return 'string';
398
        }
399
400
        return $type[0];
401
    }
402
403
    /**
404
     * Can the data type have it's size controlled within the migration?
405
     *
406
     * @param string $type
407
     * @return bool
408
     */
409
    private function typeCanDefineSize($type)
410
    {
411
        return $type == 'string' || $type == 'char';
412
    }
413
414
    /**
415
     * Extract a numeric length value from all properties specified for the attribute.
416
     *
417
     * @param array $properties
418
     * @return int $length
419
     */
420
    private function extractFieldLengthValue($properties)
421
    {
422
        foreach ($properties as $property) {
423
            if (is_numeric($property)) {
424
                return $property;
425
            }
426
        }
427
428
        return 0;
429
    }
430
431
    /**
432
     * Get the column properties that should be applied to the column.
433
     *
434
     * @param $properties
435
     * @return array
436
     */
437
    private function extractAttributePropertiesToApply($properties)
438
    {
439
        return array_intersect($properties, $this->columnProperties);
440
    }
441
442
    /**
443
     * Create a Schema Builder column.
444
     *
445
     * @param string $fieldType The type of column to create
446
     * @param string $name Name of the column to create
447
     * @param int $length Field length
448
     * @param array $traits Additional properties to apply to the column
449
     * @return string
450
     */
451
    private function buildSchemaColumn($fieldType, $name, $length = 0, $traits = [])
452
    {
453
        return sprintf("\$table->%s('%s'%s)%s;" . PHP_EOL . '            ',
454
            $fieldType,
455
            $name,
456
            $length > 0 ? ", $length" : '',
457
            implode('', array_map(function ($trait) {
458
                return '->' . $trait . '()';
459
            }, $traits))
460
        );
461
    }
462
463
    /**
464
     * Build a Model name from a word.
465
     *
466
     * @param string $name
467
     * @return string
468
     */
469
    private function modelName($name)
470
    {
471
        return ucfirst($name);
472
    }
473
474
}
475