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 | namespace Morphatic\AutoDeploy\Controllers; |
||
4 | |||
5 | use Log; |
||
6 | use Mail; |
||
7 | use Monolog\Logger; |
||
8 | use Illuminate\Http\Request; |
||
9 | use Illuminate\Routing\Controller; |
||
10 | use AdamBrett\ShellWrapper\Command\Builder as Command; |
||
11 | use AdamBrett\ShellWrapper\Command\CommandInterface; |
||
12 | use AdamBrett\ShellWrapper\Runners\Exec; |
||
13 | use Monolog\Formatter\HtmlFormatter; |
||
14 | use Monolog\Handler\SwiftMailerHandler; |
||
15 | use Morphatic\AutoDeploy\Origins\OriginInterface; |
||
16 | |||
17 | class DeployController extends Controller |
||
18 | { |
||
19 | /** |
||
20 | * The origin of the webhook request. |
||
21 | * |
||
22 | * @var Morphatic\AutoDeploy\Origins\OriginInterface |
||
23 | */ |
||
24 | private $origin; |
||
25 | |||
26 | /** |
||
27 | * The URL of the repo to be cloned. |
||
28 | * |
||
29 | * @var string |
||
30 | */ |
||
31 | private $repoUrl; |
||
32 | |||
33 | /** |
||
34 | * The absolute path of the directory on the server that contains the project. |
||
35 | * |
||
36 | * @var string |
||
37 | */ |
||
38 | private $webroot; |
||
39 | |||
40 | /** |
||
41 | * The absolute path of the directory where the new deployment will be set up. |
||
42 | * |
||
43 | * @var string |
||
44 | */ |
||
45 | private $installDir; |
||
46 | |||
47 | /** |
||
48 | * A log of the results of the entire deploy process. |
||
49 | * |
||
50 | * @var Monolog\Logger |
||
51 | */ |
||
52 | private $log; |
||
53 | |||
54 | /** |
||
55 | * The commit ID for this commit. |
||
56 | * |
||
57 | * @var string |
||
58 | */ |
||
59 | private $commitId; |
||
60 | |||
61 | /** |
||
62 | * The commit ID for this commit. |
||
63 | * |
||
64 | * @var AdamBrett\ShellWrapper\Runners\Exec |
||
65 | */ |
||
66 | private $shell; |
||
67 | |||
68 | /** |
||
69 | * The result of this commit. |
||
70 | * |
||
71 | * @var array |
||
72 | */ |
||
73 | private $result; |
||
74 | |||
75 | /** |
||
76 | * Create a new DeployController instance. |
||
77 | * |
||
78 | * @param Morphatic\AutoDeploy\Origins\OriginInterface $origin The origin of the webhook |
||
79 | * @param AdamBrett\ShellWrapper\Runners\Exec $exec The shell command execution class |
||
80 | */ |
||
81 | 36 | public function __construct(OriginInterface $origin, Exec $exec) |
|
82 | { |
||
83 | // set class variables related to the webhook origin |
||
84 | 36 | $this->origin = $origin; |
|
85 | 36 | $this->repoUrl = $this->origin->getRepoUrl(); |
|
86 | 36 | $this->commitId = $this->origin->getCommitId(); |
|
87 | |||
88 | // create an instance of the shell exec |
||
89 | 36 | $this->shell = $exec; |
|
90 | 36 | } |
|
91 | |||
92 | /** |
||
93 | * Handles incoming webhook requests. |
||
94 | */ |
||
95 | 12 | public function index() |
|
96 | { |
||
97 | // set up logging to email |
||
98 | 12 | $this->log = Log::getMonolog(); |
|
99 | 12 | if ($to = config('auto-deploy.notify')) { |
|
100 | $domain = parse_url(config('app.url'), PHP_URL_HOST); |
||
101 | $msg = \Swift_Message::newInstance('Project Deployed') |
||
102 | ->setFrom(["do_not_reply@$domain" => "Laravel Auto-Deploy[$domain]"]) |
||
103 | ->setTo($to) |
||
104 | ->setBody('', 'text/html'); |
||
105 | $handler = new SwiftMailerHandler(Mail::getSwiftMailer(), $msg, Logger::NOTICE); |
||
106 | $handler->setFormatter(new HtmlFormatter()); |
||
107 | $this->log->pushHandler($handler); |
||
108 | } |
||
109 | |||
110 | // check to see if we should execute this event |
||
111 | 12 | if (in_array($this->origin->event(), array_keys(config("auto-deploy.{$this->origin->name}")))) { |
|
112 | // get the parameters for the event we're handling |
||
113 | 12 | $configKey = "auto-deploy.{$this->origin->name}.{$this->origin->event()}"; |
|
114 | 12 | $this->webroot = config("$configKey.webroot"); |
|
115 | 12 | $this->installDir = dirname($this->webroot).'/'.date('Y-m-d').'_'.$this->commitId; |
|
116 | 12 | $steps = config("$configKey.steps"); |
|
117 | |||
118 | // execute the configured steps |
||
119 | 12 | $this->result = [ |
|
120 | 12 | 'Commit_ID' => $this->commitId, |
|
121 | 12 | 'Timestamp' => date('r'), |
|
122 | 12 | 'output' => '', |
|
123 | ]; |
||
124 | 12 | $whitelist = ['backupDatabase','pull','copyEnv','composer','npm','migrate','seed','deploy']; |
|
125 | 12 | foreach ($steps as $step) { |
|
126 | 12 | if (in_array($step, $whitelist) && !$this->{$step}()) { |
|
0 ignored issues
–
show
|
|||
127 | 4 | $this->log->error('Deploy failed.', $this->result); |
|
128 | |||
129 | 8 | return; |
|
130 | } |
||
131 | 5 | } |
|
132 | 8 | $this->log->notice('Deploy succeeded!', $this->result); |
|
133 | 4 | } else { |
|
134 | $this->log->error('Deploy failed.', ['Reason' => 'This event was not configured.']); |
||
135 | } |
||
136 | 8 | } |
|
137 | |||
138 | /** |
||
139 | * Runs a shell command, logs, and handles the result. |
||
140 | * |
||
141 | * @param AdamBrett\ShellWrapper\CommandInterface $cmd The text of the command to be run |
||
142 | * |
||
143 | * @return bool True if the command was successful, false on error |
||
144 | */ |
||
145 | 12 | private function ex(CommandInterface $cmd) |
|
146 | { |
||
147 | // try to run the command |
||
148 | 12 | $this->shell->run($cmd); |
|
149 | 12 | $output = $this->shell->getOutput(); |
|
150 | 12 | $returnValue = $this->shell->getReturnValue(); |
|
151 | |||
152 | // record the result |
||
153 | 12 | $output = count($output) ? implode("\n", $output)."\n" : ''; |
|
154 | 12 | $this->result['output'] .= "$cmd\n$output"; |
|
155 | |||
156 | // return a boolean |
||
157 | 12 | return 0 === $returnValue; |
|
158 | } |
||
159 | |||
160 | /** |
||
161 | * Backup the database. |
||
162 | * |
||
163 | * @return bool True if the database was successfully backed up. False on error. |
||
164 | */ |
||
165 | 12 | private function backupDatabase() |
|
166 | { |
||
167 | // get the name of the DB to backed up and the connection to use |
||
168 | 12 | $dbdir = database_path(); |
|
169 | 12 | $dbconn = config('database.default'); |
|
170 | 12 | $dbname = config("database.connections.$dbconn.database"); |
|
171 | |||
172 | // make a directory for the backup file and switch into that directory |
||
173 | 12 | $cmd = new Command('cd'); |
|
174 | 12 | $cmd->addParam($dbdir) |
|
175 | 12 | ->addSubCommand('&&') |
|
176 | 12 | ->addSubCommand('mkdir') |
|
177 | 12 | ->addParam('backups'); |
|
178 | 12 | if ($this->ex($cmd)) { |
|
179 | 12 | $cmd = new Command('cd'); |
|
180 | 12 | $cmd->addParam($dbdir.'/backups') |
|
181 | 12 | ->addSubCommand('&&'); |
|
182 | switch ($dbconn) { |
||
183 | 12 | case 'sqlite': |
|
184 | 2 | $cmd->addSubCommand('cp'); |
|
185 | 2 | $cmd->addParam($dbname) |
|
186 | 2 | ->addParam('.'); |
|
187 | |||
188 | 2 | return $this->ex($cmd); |
|
189 | 5 | case 'mysql': |
|
190 | 6 | $cmd->addSubCommand('mysqldump'); |
|
191 | 6 | $cmd->addParam($dbname) |
|
192 | 6 | ->addParam('>') |
|
193 | 6 | ->addParam("$dbname.sql"); |
|
194 | |||
195 | 6 | return $this->ex($cmd); |
|
196 | 3 | case 'pgsql': |
|
197 | 2 | $cmd->addSubCommand('pg_dump'); |
|
198 | 2 | $cmd->addParam($dbname) |
|
199 | 2 | ->addParam('>') |
|
200 | 2 | ->addParam("$dbname.sql"); |
|
201 | |||
202 | 2 | return $this->ex($cmd); |
|
203 | } |
||
204 | 1 | } |
|
205 | |||
206 | 2 | return false; |
|
207 | } |
||
208 | |||
209 | /** |
||
210 | * Create a new directory parallel to the webroot and clone the project into that directory. |
||
211 | * |
||
212 | * @return bool True if the clone is successful. False otherwise. |
||
213 | */ |
||
214 | 10 | private function pull() |
|
215 | { |
||
216 | 10 | if (is_writable(dirname($this->installDir))) { |
|
217 | 8 | $cmd = new Command('mkdir'); |
|
218 | 8 | $cmd->addFlag('p') |
|
219 | 8 | ->addParam($this->installDir); |
|
220 | 8 | if ($this->ex($cmd)) { |
|
221 | 8 | $cmd = new Command('cd'); |
|
222 | 8 | $cmd->addParam($this->installDir) |
|
223 | 8 | ->addSubCommand('&&') |
|
224 | 8 | ->addSubCommand('git') |
|
225 | 8 | ->addSubCommand('clone') |
|
226 | 8 | ->addParam($this->repoUrl) |
|
227 | 8 | ->addParam('.'); |
|
228 | |||
229 | 8 | return $this->ex($cmd); |
|
230 | } |
||
231 | } |
||
232 | |||
233 | 2 | return false; |
|
234 | } |
||
235 | |||
236 | /** |
||
237 | * Copy the .env file from the new deploy directory. |
||
238 | * |
||
239 | * @return bool True if the update is successful. False otherwise. |
||
240 | */ |
||
241 | 2 | private function copyEnv() |
|
0 ignored issues
–
show
function copyEnv() does not seem to conform to the naming convention (^(?:is|has|should|may|supports) ).
This check examines a number of code elements and verifies that they conform to the given naming conventions. You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods. ![]() |
|||
242 | { |
||
243 | 2 | $cmd = new Command('cp'); |
|
244 | 2 | $cmd->addParam($this->webroot.'./env') |
|
245 | 2 | ->addParam($this->installDir.'/.env'); |
|
246 | |||
247 | 2 | return $this->ex($cmd); |
|
248 | } |
||
249 | |||
250 | /** |
||
251 | * Update composer and run composer update. |
||
252 | * |
||
253 | * @return bool True if the update is successful. False otherwise. |
||
254 | */ |
||
255 | 8 | private function composer() |
|
256 | { |
||
257 | 8 | $cmd = new Command('cd'); |
|
258 | 8 | $cmd->addParam($this->installDir) |
|
259 | 8 | ->addSubCommand('&&') |
|
260 | 8 | ->addSubCommand('composer') |
|
261 | 8 | ->addParam('self-update') |
|
262 | 8 | ->addSubCommand('&&') |
|
263 | 8 | ->addSubCommand('composer') |
|
264 | 8 | ->addParam('update') |
|
265 | 8 | ->addArgument('no-interaction'); |
|
266 | |||
267 | 8 | return $this->ex($cmd); |
|
268 | } |
||
269 | |||
270 | /** |
||
271 | * Run npm update. |
||
272 | * |
||
273 | * @return bool True if npm is successful. False otherwise. |
||
274 | */ |
||
275 | 8 | private function npm() |
|
276 | { |
||
277 | 8 | $cmd = new Command('cd'); |
|
278 | 8 | $cmd->addParam($this->installDir) |
|
279 | 8 | ->addSubCommand('&&') |
|
280 | 8 | ->addSubCommand('npm') |
|
281 | 8 | ->addParam('update'); |
|
282 | |||
283 | 8 | return $this->ex($cmd); |
|
284 | } |
||
285 | |||
286 | /** |
||
287 | * Run any necessary database migrations. |
||
288 | * |
||
289 | * @return bool True if the migration is successful. False otherwise. |
||
290 | */ |
||
291 | 8 | private function migrate() |
|
292 | { |
||
293 | 8 | $cmd = new Command('cd'); |
|
294 | 8 | $cmd->addParam($this->installDir) |
|
295 | 8 | ->addSubCommand('&&') |
|
296 | 8 | ->addSubCommand('php') |
|
297 | 8 | ->addSubCommand('artisan') |
|
298 | 8 | ->addParam('migrate') |
|
299 | 8 | ->addArgument('force') |
|
300 | 8 | ->addArgument('no-interaction'); |
|
301 | |||
302 | 8 | return $this->ex($cmd); |
|
303 | } |
||
304 | |||
305 | /** |
||
306 | * Run any necessary database migrations. |
||
307 | * |
||
308 | * @return bool True if the migration is successful. False otherwise. |
||
309 | */ |
||
310 | 8 | private function seed() |
|
311 | { |
||
312 | 8 | $cmd = new Command('cd'); |
|
313 | 8 | $cmd->addParam($this->installDir) |
|
314 | 8 | ->addSubCommand('&&') |
|
315 | 8 | ->addSubCommand('php') |
|
316 | 8 | ->addSubCommand('artisan') |
|
317 | 8 | ->addParam('db:seed'); |
|
318 | |||
319 | 8 | return $this->ex($cmd); |
|
320 | } |
||
321 | |||
322 | /** |
||
323 | * Symlinks the new deploy directory to the webroot. |
||
324 | * |
||
325 | * @return bool True if the symlink is successful. False otherwise. |
||
326 | */ |
||
327 | 8 | private function deploy() |
|
328 | { |
||
329 | 8 | $cmd = new Command('cd'); |
|
330 | 8 | $cmd->addParam(dirname($this->webroot)) |
|
331 | 8 | ->addSubCommand('&&') |
|
332 | 8 | ->addSubCommand('ln') |
|
333 | 8 | ->addFlag('fs') |
|
334 | 8 | ->addParam($this->installDir) |
|
335 | 8 | ->addParam($this->webroot); |
|
336 | |||
337 | 8 | return $this->ex($cmd); |
|
338 | } |
||
339 | } |
||
340 |
$step
can contain request data and is used in code execution context(s) leading to a potential security vulnerability.General Strategies to prevent injection
In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:
For numeric data, we recommend to explicitly cast the data: