fix: harden mx helper and cleanup flow
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once PLUGIN_ROOT . '/mx-helper/cleanup-expired-login-urls.php';
|
||||
|
||||
test('cleanup deletes only expired login urls matching local mail-login state', function (): void {
|
||||
$state = [
|
||||
'timestamp' => 1782810000,
|
||||
'expires' => 1782810030,
|
||||
'correlation' => 'autologin-h4-mxtest-1782810000',
|
||||
'redirect_path' => '/',
|
||||
'client_ip' => '198.51.100.10',
|
||||
'ip_bound' => 1,
|
||||
'status' => 'created',
|
||||
];
|
||||
|
||||
$matching = [
|
||||
'id' => 'HASHURLMATCH',
|
||||
'created' => '2026-06-30T10:00:00Z',
|
||||
'expires' => '2026-06-30T10:00:30Z',
|
||||
'redirectURL' => '/',
|
||||
'allowNetworks' => ['198.51.100.10/32'],
|
||||
];
|
||||
$wrongRedirect = $matching;
|
||||
$wrongRedirect['id'] = 'HASHURLWRONG';
|
||||
$wrongRedirect['redirectURL'] = '/admin';
|
||||
$notExpired = $matching;
|
||||
$notExpired['id'] = 'HASHURLFRESH';
|
||||
$notExpired['expires'] = '2099-06-30T10:00:30Z';
|
||||
|
||||
assert_true(mailLoginCleanupShouldDelete($matching, [$state], strtotime('2026-06-30T10:01:00Z')));
|
||||
assert_false(mailLoginCleanupShouldDelete($wrongRedirect, [$state], strtotime('2026-06-30T10:01:00Z')));
|
||||
assert_false(mailLoginCleanupShouldDelete($notExpired, [$state], strtotime('2026-06-30T10:01:00Z')));
|
||||
});
|
||||
|
||||
@@ -12,10 +12,11 @@ test('mx helper conditionally passes user ip binding to da login-url', function
|
||||
|
||||
$script = PLUGIN_ROOT . '/mx-helper/mail-login-authorized-command.sh';
|
||||
$cmd = sprintf(
|
||||
'MAIL_LOGIN_DA_BIN=%s MAIL_LOGIN_AUDIT_LOG=%s MAIL_LOGIN_STATE_DIR=%s %s %s',
|
||||
'MAIL_LOGIN_DA_BIN=%s MAIL_LOGIN_AUDIT_LOG=%s MAIL_LOGIN_STATE_DIR=%s MAIL_LOGIN_ALLOWED_SOURCE_HOSTS=%s %s %s',
|
||||
escapeshellarg($bin),
|
||||
escapeshellarg($audit),
|
||||
escapeshellarg($state),
|
||||
escapeshellarg('h4.domena.pl'),
|
||||
escapeshellarg($script),
|
||||
'mail-login-create-url mxtest h4.domena.pl h4 30 1 198.51.100.10 /'
|
||||
);
|
||||
@@ -26,10 +27,11 @@ test('mx helper conditionally passes user ip binding to da login-url', function
|
||||
|
||||
file_put_contents($log, '');
|
||||
$cmdNoIp = sprintf(
|
||||
'MAIL_LOGIN_DA_BIN=%s MAIL_LOGIN_AUDIT_LOG=%s MAIL_LOGIN_STATE_DIR=%s %s %s',
|
||||
'MAIL_LOGIN_DA_BIN=%s MAIL_LOGIN_AUDIT_LOG=%s MAIL_LOGIN_STATE_DIR=%s MAIL_LOGIN_ALLOWED_SOURCE_HOSTS=%s %s %s',
|
||||
escapeshellarg($bin),
|
||||
escapeshellarg($audit),
|
||||
escapeshellarg($state),
|
||||
escapeshellarg('h4.domena.pl'),
|
||||
escapeshellarg($script),
|
||||
'mail-login-create-url mxtest h4.domena.pl h4 30 0 198.51.100.10 /'
|
||||
);
|
||||
@@ -50,11 +52,12 @@ test('mx helper accepts forced command arguments from ssh original command', fun
|
||||
|
||||
$script = PLUGIN_ROOT . '/mx-helper/mail-login-authorized-command.sh';
|
||||
$cmd = sprintf(
|
||||
'MAIL_LOGIN_DA_BIN=%s MAIL_LOGIN_AUDIT_LOG=%s MAIL_LOGIN_STATE_DIR=%s SSH_ORIGINAL_COMMAND=%s %s',
|
||||
'MAIL_LOGIN_DA_BIN=%s MAIL_LOGIN_AUDIT_LOG=%s MAIL_LOGIN_STATE_DIR=%s MAIL_LOGIN_ALLOWED_SOURCE_HOSTS=%s SSH_ORIGINAL_COMMAND=%s %s',
|
||||
escapeshellarg($bin),
|
||||
escapeshellarg($audit),
|
||||
escapeshellarg($state),
|
||||
escapeshellarg('mail-login-create-url mxtest h4.domena.pl h4 30 1 198.51.100.10 /'),
|
||||
escapeshellarg('h4.domena.pl'),
|
||||
escapeshellarg('mail-login-create-url mxtest h4.domena.pl evil 30 1 198.51.100.10 /'),
|
||||
escapeshellarg($script)
|
||||
);
|
||||
exec($cmd, $out, $code);
|
||||
@@ -62,4 +65,38 @@ test('mx helper accepts forced command arguments from ssh original command', fun
|
||||
assert_same(0, $code, implode("\n", $out));
|
||||
assert_contains('--user=mxtest', file_get_contents($log) ?: '');
|
||||
assert_contains('autologin-h4-mxtest-', file_get_contents($audit) ?: '');
|
||||
assert_not_contains('autologin-evil-mxtest-', file_get_contents($audit) ?: '');
|
||||
});
|
||||
|
||||
test('mx helper rejects disallowed source hosts and invalid client ips', function (): void {
|
||||
$bin = TEST_TMP . '/fake-da-reject';
|
||||
$audit = TEST_TMP . '/audit-reject.log';
|
||||
$state = TEST_TMP . '/state-reject';
|
||||
test_write($bin, "#!/bin/sh\necho 'URL: https://mx1.domena.pl:2222/api/login/url?key=SECRET'\n");
|
||||
chmod($bin, 0755);
|
||||
$script = PLUGIN_ROOT . '/mx-helper/mail-login-authorized-command.sh';
|
||||
|
||||
$badHost = sprintf(
|
||||
'MAIL_LOGIN_DA_BIN=%s MAIL_LOGIN_AUDIT_LOG=%s MAIL_LOGIN_STATE_DIR=%s MAIL_LOGIN_ALLOWED_SOURCE_HOSTS=%s %s %s 2>/dev/null',
|
||||
escapeshellarg($bin),
|
||||
escapeshellarg($audit),
|
||||
escapeshellarg($state),
|
||||
escapeshellarg('h4.domena.pl'),
|
||||
escapeshellarg($script),
|
||||
'mail-login-create-url mxtest evil.domena.pl evil 30 1 198.51.100.10 /'
|
||||
);
|
||||
exec($badHost, $outHost, $codeHost);
|
||||
assert_same(1, $codeHost);
|
||||
|
||||
$badIp = sprintf(
|
||||
'MAIL_LOGIN_DA_BIN=%s MAIL_LOGIN_AUDIT_LOG=%s MAIL_LOGIN_STATE_DIR=%s MAIL_LOGIN_ALLOWED_SOURCE_HOSTS=%s %s %s 2>/dev/null',
|
||||
escapeshellarg($bin),
|
||||
escapeshellarg($audit),
|
||||
escapeshellarg($state),
|
||||
escapeshellarg('h4.domena.pl'),
|
||||
escapeshellarg($script),
|
||||
'mail-login-create-url mxtest h4.domena.pl h4 30 1 999.999.999.999 /'
|
||||
);
|
||||
exec($badIp, $outIp, $codeIp);
|
||||
assert_same(1, $codeIp);
|
||||
});
|
||||
|
||||
@@ -8,6 +8,8 @@ test('settings expose secure defaults from plugin-settings.conf', function (): v
|
||||
|
||||
assert_same(30, $settings->loginKeyTtl());
|
||||
assert_true($settings->userIpBound());
|
||||
assert_true($settings->defaultEnable());
|
||||
assert_same('login_url', $settings->method());
|
||||
assert_same('root', $settings->sshUser());
|
||||
assert_same('https://mx1.mojserwer.pl:2222', $settings->targetBaseUrl());
|
||||
});
|
||||
@@ -41,3 +43,14 @@ test('settings parse allowed source hosts as normalized hostnames', function ():
|
||||
assert_same(['h4.domena.pl', 'serwer2.example.net'], $settings->allowedSourceHosts());
|
||||
});
|
||||
|
||||
test('settings reject disabled plugin and unsupported methods through access guard', function (): void {
|
||||
try {
|
||||
Settings::fromArray(['MAIL_LOGIN_METHOD' => 'password']);
|
||||
throw new RuntimeException('Expected unsupported method to fail');
|
||||
} catch (InvalidArgumentException $e) {
|
||||
assert_contains('MAIL_LOGIN_METHOD', $e->getMessage());
|
||||
}
|
||||
|
||||
$settings = Settings::fromArray(['DEFAULT_ENABLE' => 'false']);
|
||||
assert_false($settings->defaultEnable());
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user