Skip to content

Commit 0222aa1

Browse files
committed
feat(api): add endpoint to execute a command
1 parent 4d36bc4 commit 0222aa1

File tree

2 files changed

+33
-10
lines changed

2 files changed

+33
-10
lines changed

app/Http/Controllers/Api/ApplicationsController.php

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2596,34 +2596,57 @@ public function action_restart(Request $request)
25962596
)]
25972597
public function execute_command_by_uuid(Request $request)
25982598
{
2599-
$data = $request->validate([
2600-
'command' => 'required|string|max:255',
2601-
]);
2599+
// TODO: Need to review this from security perspective, to not allow arbitrary command execution
2600+
$allowedFields = ['command'];
26022601
$teamId = getTeamIdFromToken();
26032602
if (is_null($teamId)) {
26042603
return invalidTokenResponse();
26052604
}
26062605
$uuid = $request->route('uuid');
2607-
if (!$uuid) {
2606+
if (! $uuid) {
26082607
return response()->json(['message' => 'UUID is required.'], 400);
26092608
}
26102609
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
2611-
if (!$application) {
2610+
if (! $application) {
26122611
return response()->json(['message' => 'Application not found.'], 404);
26132612
}
2613+
$return = validateIncomingRequest($request);
2614+
if ($return instanceof \Illuminate\Http\JsonResponse) {
2615+
return $return;
2616+
}
2617+
$validator = customApiValidator($request->all(), [
2618+
'command' => 'string|required',
2619+
]);
2620+
2621+
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
2622+
if ($validator->fails() || ! empty($extraFields)) {
2623+
$errors = $validator->errors();
2624+
if (! empty($extraFields)) {
2625+
foreach ($extraFields as $field) {
2626+
$errors->add($field, 'This field is not allowed.');
2627+
}
2628+
}
2629+
2630+
return response()->json([
2631+
'message' => 'Validation failed.',
2632+
'errors' => $errors,
2633+
], 422);
2634+
}
26142635

26152636
$container = getCurrentApplicationContainerStatus($application->destination->server, $application->id)->firstOrFail();
26162637
$status = getContainerStatus($application->destination->server, $container['Names']);
26172638

2618-
if ('running' !== $status) {
2619-
return;
2639+
if ($status !== 'running') {
2640+
return response()->json([
2641+
'message' => 'Application is not running.',
2642+
], 400);
26202643
}
26212644

26222645
$commands = collect([
2623-
executeInDocker($container['Names'], $data['command']),
2646+
executeInDocker($container['Names'], $request->command),
26242647
]);
26252648

2626-
$res = instant_remote_process($commands, $application->destination->server);
2649+
$res = instant_remote_process(command: $commands, server: $application->destination->server);
26272650

26282651
return response()->json([
26292652
'message' => 'Command executed.',

routes/api.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
Route::patch('/applications/{uuid}/envs/bulk', [ApplicationsController::class, 'create_bulk_envs'])->middleware([IgnoreReadOnlyApiToken::class]);
8787
Route::patch('/applications/{uuid}/envs', [ApplicationsController::class, 'update_env_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]);
8888
Route::delete('/applications/{uuid}/envs/{env_uuid}', [ApplicationsController::class, 'delete_env_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]);
89-
Route::post('/applications/{uuid}/execute', [ApplicationsController::class, 'execute_command_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]);
89+
// Route::post('/applications/{uuid}/execute', [ApplicationsController::class, 'execute_command_by_uuid'])->middleware([OnlyRootApiToken::class]);
9090

9191
Route::match(['get', 'post'], '/applications/{uuid}/start', [ApplicationsController::class, 'action_deploy'])->middleware([IgnoreReadOnlyApiToken::class]);
9292
Route::match(['get', 'post'], '/applications/{uuid}/restart', [ApplicationsController::class, 'action_restart'])->middleware([IgnoreReadOnlyApiToken::class]);

0 commit comments

Comments
 (0)