Skip to content

Commit ee11a56

Browse files
committed
Error when XBoard opponent has newline in name
Use the configuration machinery to send the opponent's information to the engine instead of using the Opponent class directly. This will make sure that an exception is raised if the opponent's name has newlines in it before send_line() is called. This prevents opponents from injecting commands into the engine. For example, if an opponent sets their name as "Cheat\neasy", then the following commands will be sent to the local engine name Cheat easy which would cause the engine to turn off pondering. Also, add tests to show the change works as expected.
1 parent 77bb7b2 commit ee11a56

2 files changed

Lines changed: 17 additions & 6 deletions

File tree

chess/engine.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2152,6 +2152,7 @@ def _variant(self, variant: Optional[str]) -> None:
21522152

21532153
def _new(self, board: chess.Board, game: object, options: ConfigMapping, opponent: Optional[Opponent] = None) -> None:
21542154
self._configure(options)
2155+
self._configure(self._opponent_configuration(opponent=opponent))
21552156

21562157
# Set up starting position.
21572158
root = board.root()
@@ -2172,15 +2173,16 @@ def _new(self, board: chess.Board, game: object, options: ConfigMapping, opponen
21722173
if self.config.get("random"):
21732174
self.send_line("random")
21742175

2175-
opponent_name = (opponent.name if opponent else None) or self.target_config.get("name")
2176+
opponent_name = self.config.get("name")
21762177
if opponent_name and self.features.get("name", True):
21772178
self.send_line(f"name {opponent_name}")
21782179

2179-
opponent_rating = (opponent.rating if opponent else None) or self.target_config.get("opponent_rating") or 0
2180-
if self.target_config.get("engine_rating") or opponent_rating:
2181-
self.send_line(f"rating {self.target_config.get('engine_rating') or 0} {opponent_rating}")
2180+
opponent_rating = self.config.get("opponent_rating")
2181+
engine_rating = self.config.get("engine_rating")
2182+
if engine_rating or opponent_rating:
2183+
self.send_line(f"rating {engine_rating or 0} {opponent_rating or 0}")
21822184

2183-
if (opponent and opponent.is_engine) or (self.target_config.get("computer") if self.config.get("computer") is None else self.config.get("computer")):
2185+
if self.config.get("computer"):
21842186
self.send_line("computer")
21852187

21862188
self.send_line("force")
@@ -2518,7 +2520,7 @@ def _opponent_configuration(self, *, opponent: Optional[Opponent] = None, engine
25182520
if opponent is None:
25192521
return {}
25202522

2521-
opponent_info: Dict[str, Union[int, bool, str]] = {"engine_rating": engine_rating or 0,
2523+
opponent_info: Dict[str, Union[int, bool, str]] = {"engine_rating": engine_rating or self.target_config.get("engine_rating") or 0,
25222524
"opponent_rating": opponent.rating or 0,
25232525
"computer": opponent.is_engine or False}
25242526

test.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3688,6 +3688,15 @@ async def main():
36883688
self.assertEqual(result.move, board.parse_san("e5"))
36893689
mock.assert_done()
36903690

3691+
bad_opponent = chess.engine.Opponent("New\nLine", "GM", 1, False)
3692+
with self.assertRaises(chess.engine.EngineError):
3693+
await protocol.send_opponent_information(opponent=bad_opponent)
3694+
mock.assert_done()
3695+
3696+
with self.assertRaises(chess.engine.EngineError):
3697+
result = await protocol.play(board, limit, game="bad game", opponent=bad_opponent)
3698+
mock.assert_done()
3699+
36913700
asyncio.set_event_loop_policy(chess.engine.EventLoopPolicy())
36923701
asyncio.run(main())
36933702

0 commit comments

Comments
 (0)