feat(facade): append rcon_password as final server.cfg line

Source cvar semantics are last-wins; appending the rcon_password after
all overlay exec lines and blueprint config ensures no overlay or user
config line can silently override it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mwiegand 2026-05-12 21:40:56 +02:00
parent 83d2a9932c
commit 2a440dae45
No known key found for this signature in database
2 changed files with 48 additions and 1 deletions

View file

@ -44,11 +44,16 @@ def build_server_spec_payload(
for overlay_id, _, expose in reversed(overlay_rows)
if expose
]
config_lines: list[str] = exec_lines + json.loads(blueprint.config)
# rcon_password is appended LAST so neither overlays nor user blueprint
# config can override it (Source's cvar semantics are last-wins).
if server.rcon_password:
config_lines.append(f'rcon_password "{server.rcon_password}"')
return {
"port": server.port,
"overlays": overlays,
"arguments": json.loads(blueprint.arguments),
"config": exec_lines + json.loads(blueprint.config),
"config": config_lines,
}

View file

@ -343,3 +343,45 @@ def test_initialize_fails_fast_on_uncached_workshop_items(
assert all("initialize" not in cmd for cmd in invocations), invocations
# ---------------------------------------------------------------------------
# build_server_spec_payload — rcon_password injection
# ---------------------------------------------------------------------------
def _make_server_blueprint(rcon: str = "") -> tuple[Server, Blueprint]:
bp = Blueprint(
id=1, user_id=1, name="bp",
arguments='["-tickrate","60"]',
config='["sv_consistency 1","mp_gamemode coop"]',
)
srv = Server(
id=1, user_id=1, blueprint_id=1, name="s", port=27500,
rcon_password=rcon,
)
return srv, bp
def test_build_server_spec_payload_appends_rcon_password_last() -> None:
from l4d2web.services.l4d2_facade import build_server_spec_payload
srv, bp = _make_server_blueprint(rcon="topsecret123")
overlays = [(7, "/overlays/7", True), (8, "/overlays/8", False)]
spec = build_server_spec_payload(srv, bp, overlays)
cfg = spec["config"]
# rcon_password line is the LAST entry — overlay exec lines + blueprint
# config + rcon_password.
assert cfg[-1] == 'rcon_password "topsecret123"'
# Lines before our injection still contain the blueprint config.
assert "sv_consistency 1" in cfg
assert "mp_gamemode coop" in cfg
def test_build_server_spec_payload_omits_rcon_password_when_empty() -> None:
from l4d2web.services.l4d2_facade import build_server_spec_payload
srv, bp = _make_server_blueprint(rcon="")
spec = build_server_spec_payload(srv, bp, [])
for line in spec["config"]:
assert not line.startswith("rcon_password ")