Merge pull request #476 from PR0M3TH3AN/codex/enforce-field-restrictions-for-entry-modification

Fix entry type validation
This commit is contained in:
thePR0M3TH3AN
2025-07-12 13:06:35 -04:00
committed by GitHub
6 changed files with 155 additions and 20 deletions

View File

@@ -723,6 +723,93 @@ class EntryManager:
entry_type = entry.get("type", entry.get("kind", EntryType.PASSWORD.value))
provided_fields = {
"username": username,
"url": url,
"archived": archived,
"notes": notes,
"label": label,
"period": period,
"digits": digits,
"value": value,
"custom_fields": custom_fields,
"tags": tags,
}
allowed = {
EntryType.PASSWORD.value: {
"username",
"url",
"label",
"archived",
"notes",
"custom_fields",
"tags",
},
EntryType.TOTP.value: {
"label",
"period",
"digits",
"archived",
"notes",
"custom_fields",
"tags",
},
EntryType.KEY_VALUE.value: {
"label",
"value",
"archived",
"notes",
"custom_fields",
"tags",
},
EntryType.MANAGED_ACCOUNT.value: {
"label",
"value",
"archived",
"notes",
"custom_fields",
"tags",
},
EntryType.SSH.value: {
"label",
"archived",
"notes",
"custom_fields",
"tags",
},
EntryType.PGP.value: {
"label",
"archived",
"notes",
"custom_fields",
"tags",
},
EntryType.NOSTR.value: {
"label",
"archived",
"notes",
"custom_fields",
"tags",
},
EntryType.SEED.value: {
"label",
"archived",
"notes",
"custom_fields",
"tags",
},
}
allowed_fields = allowed.get(entry_type, set())
invalid = {
k for k, v in provided_fields.items() if v is not None
} - allowed_fields
if invalid:
raise ValueError(
f"Entry type '{entry_type}' does not support fields: {', '.join(sorted(invalid))}"
)
if entry_type == EntryType.TOTP.value:
if label is not None:
entry["label"] = label
@@ -796,6 +883,7 @@ class EntryManager:
print(
colored(f"Error: Failed to modify entry at index {index}: {e}", "red")
)
raise
def archive_entry(self, index: int) -> None:
"""Mark the specified entry as archived."""

View File

@@ -207,16 +207,19 @@ def update_entry(
"""
_check_token(authorization)
assert _pm is not None
_pm.entry_manager.modify_entry(
entry_id,
username=entry.get("username"),
url=entry.get("url"),
notes=entry.get("notes"),
label=entry.get("label"),
period=entry.get("period"),
digits=entry.get("digits"),
value=entry.get("value"),
)
try:
_pm.entry_manager.modify_entry(
entry_id,
username=entry.get("username"),
url=entry.get("url"),
notes=entry.get("notes"),
label=entry.get("label"),
period=entry.get("period"),
digits=entry.get("digits"),
value=entry.get("value"),
)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
return {"status": "ok"}

View File

@@ -306,16 +306,20 @@ def entry_modify(
) -> None:
"""Modify an existing entry."""
pm = _get_pm(ctx)
pm.entry_manager.modify_entry(
entry_id,
username=username,
url=url,
notes=notes,
label=label,
period=period,
digits=digits,
value=value,
)
try:
pm.entry_manager.modify_entry(
entry_id,
username=username,
url=url,
notes=notes,
label=label,
period=period,
digits=digits,
value=value,
)
except ValueError as e:
typer.echo(str(e))
raise typer.Exit(code=1)
pm.sync_vault()

View File

@@ -93,6 +93,19 @@ def test_create_and_modify_ssh_entry(client):
assert calls["modify"][1]["notes"] == "x"
def test_update_entry_error(client):
cl, token = client
def modify(*a, **k):
raise ValueError("nope")
api._pm.entry_manager.modify_entry = modify
headers = {"Authorization": f"Bearer {token}"}
res = cl.put("/api/v1/entry/1", json={"username": "x"}, headers=headers)
assert res.status_code == 400
assert res.json() == {"detail": "nope"}
def test_update_config_secret_mode(client):
cl, token = client
called = {}

View File

@@ -1,4 +1,5 @@
from helpers import create_vault, TEST_SEED, TEST_PASSWORD
import pytest
from password_manager.entry_management import EntryManager
from password_manager.backup import BackupManager
@@ -18,3 +19,14 @@ def test_modify_totp_entry_period_digits_and_archive(tmp_path):
assert entry["period"] == 60
assert entry["digits"] == 8
assert entry["archived"] is True
def test_modify_totp_entry_invalid_field(tmp_path):
vault, _ = create_vault(tmp_path, TEST_SEED, TEST_PASSWORD)
cfg_mgr = ConfigManager(vault, tmp_path)
backup_mgr = BackupManager(tmp_path, cfg_mgr)
em = EntryManager(vault, backup_mgr)
em.add_totp("Example", TEST_SEED)
with pytest.raises(ValueError):
em.modify_entry(0, username="alice")

View File

@@ -396,6 +396,21 @@ def test_entry_modify(monkeypatch):
assert called["args"][:5] == (1, "alice", None, None, None)
def test_entry_modify_invalid(monkeypatch):
def modify_entry(*a, **k):
raise ValueError("bad")
pm = SimpleNamespace(
entry_manager=SimpleNamespace(modify_entry=modify_entry),
select_fingerprint=lambda fp: None,
sync_vault=lambda: None,
)
monkeypatch.setattr(cli, "PasswordManager", lambda: pm)
result = runner.invoke(app, ["entry", "modify", "1", "--username", "alice"])
assert result.exit_code == 1
assert "bad" in result.stdout
def test_entry_archive(monkeypatch):
called = {}