Validate external core shutdown timeouts

Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
Loren Eteval
2026-09-19 20:48:10 +08:00
parent 87597d7ffd
commit aa6bd6dcde
3 changed files with 24 additions and 3 deletions
@@ -232,9 +232,14 @@ class ConfigExternalCore(CoreConfiguration):
'Shutdown timeout must be a number of seconds'
)
value = float(value)
try:
value = float(value)
except OverflowError as ex:
raise ExternalCoreConfigurationError(
'Shutdown timeout must be between 0.1 and 60 seconds'
) from ex
if value < 0.1 or value > 60:
if not 0.1 <= value <= 60:
raise ExternalCoreConfigurationError(
'Shutdown timeout must be between 0.1 and 60 seconds'
)
+1 -1
View File
@@ -91,7 +91,7 @@ worker. Choose tests by the changed contract rather than by filename alone.
| [test_connection_startup_async.py](test_connection_startup_async.py) | Real local-listener readiness, timeout/cancel/replacement, semantic exits, DNS reply lifetime, mocked platform-specific TUN sequencing. |
| [test_controllers.py](test_controllers.py) | Connection state/error/reconnect transitions, startup restoration, shared settings, routing fallback persistence and tray/selector agreement after custom-routing disable/re-enable. |
| [test_runtime_lifecycle.py](test_runtime_lifecycle.py) | Qt-thread exit dispatch, commit/exit races, duplicate and late exits, idempotent release, spawn failure, queue/timer disposal on preparation failure. |
| [test_external_core.py](test_external_core.py) | Harmless real process launch/output/shutdown, partial thread-start rollback, readiness/TUN metadata, Windows paths with spaces, subscription rejection of executable profiles, bounded DNS references. |
| [test_external_core.py](test_external_core.py) | Harmless real process launch/output/shutdown, partial thread-start rollback, non-finite timeout rejection, readiness/TUN metadata, Windows paths with spaces, subscription rejection of executable profiles, bounded DNS references. |
| [test_frozenlib.py](test_frozenlib.py) | Nested state guards, cleanup isolation, bounded caches/throttling, dual-stack probe selection, mocked proxy/DNS/routes/startup/session boundaries and failure handling. |
| [test_native_tun_semantics.py](test_native_tun_semantics.py) | Xray/Hysteria2 runtime-copy TUN preservation/replacement, managed-TUN failures, download-test stripping, prevention of a second tun2socks owner. |
| [test_subscription_sync.py](test_subscription_sync.py) | Group-local preparation/commit, stable duplicate identity, atomic failure, preservation of newer local metadata, rejection of changed source state. |
+16
View File
@@ -101,6 +101,22 @@ class ExternalCoreProcessTest(unittest.TestCase):
}
)
def testShutdownTimeoutRejectsNonFiniteAndOverflowingValues(self):
"""Reject invalid wait values before any executable can be acquired."""
for value in (float('nan'), float('inf'), -float('inf'), 10**400):
with self.subTest(value=str(value)[:20]):
config = self.configuration(['-c', 'pass'], str(Path.cwd()))
config['shutdownTimeout'] = value
self.assertTrue(config.validateProcess())
runtime = ExternalCoreProcess(config)
with mock.patch(
'Furious.Backends.ExternalCore.Process.subprocess.Popen'
) as spawn:
with self.assertRaises(RuntimeStartError):
runtime.start()
spawn.assert_not_called()
runtime.dispose()
def testDisposedRuntimeCannotAcquireAnotherProcess(self):
"""Disposal is terminal even when the stored launch specification is valid."""
runtime = ExternalCoreProcess(