fix(api-v3): reject unknown restore option keys

CodeRabbit's review of the blueprint split (#553) asked that
POST /backup/restore reject option keys outside RestoreOptions'
known set. The follow-up commit fixed the bool("false")-is-True
bug with _coerce_to_bool but never added the key check: a typo'd
or renamed key (e.g. "restoreSecrets") is silently ignored by
opts_dict.get(key, True), so the flag stays at its True default
and secrets get restored despite the caller's request saying
otherwise -- with no indication anything was wrong.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vmcwf5vMgYqdt8bJTZtiwb
This commit is contained in:
Claude
2026-09-11 09:31:21 +00:00
parent 4e91cf1fef
commit 6e06b60615
2 changed files with 31 additions and 0 deletions
@@ -139,6 +139,23 @@ class TestRequestValidation:
assert post(client, options="{}").status_code == 200
assert restore.call_args[0][2].restore_config is True
def test_unknown_option_key_is_refused(self, client, restore):
# Regression: opts_dict.get('restore_secrets', True) silently
# ignores a typo'd/renamed key like "restoreSecrets" and keeps the
# True default, restoring secrets a caller's request clearly meant
# to exclude -- with no indication anything was wrong.
response = post(client, options=json.dumps({"restoreSecrets": False}))
assert response.status_code == 400
assert "Unknown restore option" in response.get_json()["message"]
assert "restoreSecrets" in response.get_json()["message"]
restore.assert_not_called()
def test_known_and_unknown_keys_together_are_refused(self, client, restore):
response = post(client, options=json.dumps({
"restore_secrets": False, "restore_everything": True}))
assert response.status_code == 400
restore.assert_not_called()
class TestOptionsAreBooleanAware:
"""Regression: bool("false") is True in Python.