fix(sentinel): validate push containers payload
Reject malformed sentinel push payloads before updating heartbeat state, dispatching jobs, or writing deduplication cache entries.
This commit is contained in:
parent
ed3780b2a7
commit
7677fac2f5
2 changed files with 33 additions and 0 deletions
|
|
@ -8,6 +8,7 @@
|
|||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class SentinelController extends Controller
|
||||
{
|
||||
|
|
@ -77,6 +78,17 @@ public function push(Request $request)
|
|||
|
||||
return response()->json(['message' => 'Unauthorized'], 401);
|
||||
}
|
||||
$validator = Validator::make($request->all(), [
|
||||
'containers' => ['required', 'array', 'min:1'],
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json(serializeApiResponse([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => $validator->errors(),
|
||||
]), 422);
|
||||
}
|
||||
|
||||
$data = $request->all();
|
||||
|
||||
// Heartbeat MUST update on every push — drives isSentinelLive() and SSH-check skipping.
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
config(['app.maintenance.store' => 'array']);
|
||||
|
||||
Queue::fake();
|
||||
Cache::flush();
|
||||
|
||||
|
|
@ -69,6 +71,25 @@ function sentinelPayload(array $containers, ?float $diskPercentage = 42.0): arra
|
|||
expect(Carbon::parse($this->server->fresh()->sentinel_updated_at)->diffInSeconds(now()))->toBeLessThan(5);
|
||||
});
|
||||
|
||||
it('rejects malformed sentinel payloads before touching server state', function (array $payload) {
|
||||
$this->server->update(['sentinel_updated_at' => now()->subHour()]);
|
||||
$originalHeartbeat = $this->server->fresh()->sentinel_updated_at;
|
||||
|
||||
pushSentinel($this->token, $payload)
|
||||
->assertUnprocessable()
|
||||
->assertJsonPath('message', 'Validation failed.')
|
||||
->assertJsonValidationErrors('containers');
|
||||
|
||||
Queue::assertNotPushed(PushServerUpdateJob::class);
|
||||
expect($this->server->fresh()->sentinel_updated_at)->toBe($originalHeartbeat);
|
||||
expect(Cache::has('sentinel:push-hash:'.$this->server->id))->toBeFalse();
|
||||
expect(Cache::has('sentinel:push-force:'.$this->server->id))->toBeFalse();
|
||||
})->with([
|
||||
'missing containers' => [[]],
|
||||
'non-array containers' => [['containers' => 'not-an-array']],
|
||||
'empty containers' => [['containers' => []]],
|
||||
]);
|
||||
|
||||
it('dispatches the job when container state changes', function () use ($running) {
|
||||
pushSentinel($this->token, sentinelPayload($running()))->assertOk();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue