"""Root passwords entered through the UI become encrypted installer hashes.""" import json from passlib.hash import sha512_crypt import pytest from provisioner.security import Security from test_acceptance import PASSWORD, environment, login, post def test_root_password_secret_is_hashed_encrypted_and_never_returned(environment): app, client, csrf = environment password = " Installer-password-2026! " created = post(client, "/api/v1/secrets", { "name": "Installer root password", "kind": "root_password", "value": password, }, csrf) assert set(created) == {"id", "name"} with app.state.db.connection() as connection: row = connection.execute("SELECT * FROM secrets WHERE id=?", (created["id"],)).fetchone() audit_data = json.dumps([dict(entry) for entry in connection.execute("SELECT * FROM audit")]) stored = app.state.security.decrypt(row["ciphertext"]) assert stored.startswith("$6$rounds=656000$") assert sha512_crypt.verify(password, stored) assert not sha512_crypt.verify(password.strip(), stored) assert password not in row["ciphertext"] and stored not in row["ciphertext"] public_data = json.dumps(created) + client.get("/api/v1/secrets").text + audit_data assert password not in public_data and stored not in public_data @pytest.mark.parametrize("password", ["short", "x" * 1025, "Valid-password\x00invalid"]) def test_root_password_secret_rejects_invalid_password_without_echo(environment, password): app, client, csrf = environment response = client.post("/api/v1/secrets", json={ "name": "Invalid root password", "kind": "root_password", "value": password, }, headers=csrf) assert response.status_code == 422 assert password not in response.text with app.state.db.connection() as connection: assert connection.execute("SELECT COUNT(*) FROM secrets").fetchone()[0] == 0 def test_raw_secret_default_preserves_existing_behavior(environment): app, client, csrf = environment created = post(client, "/api/v1/secrets", {"name": "Raw value", "value": " unchanged-value "}, csrf) with app.state.db.connection() as connection: row = connection.execute("SELECT ciphertext FROM secrets WHERE id=?", (created["id"],)).fetchone() assert app.state.security.decrypt(row["ciphertext"]) == "unchanged-value" assert client.post("/api/v1/secrets", json={"name": "Empty raw value", "value": " "}, headers=csrf).status_code == 422 @pytest.mark.parametrize("role, metadata_status", [("author", 200), ("operator", 200), ("reader", 403)]) def test_secret_metadata_roles_cannot_create_or_read_secret_values(environment, role, metadata_status): app, client, csrf = environment created = post(client, "/api/v1/secrets", {"name": "Private root reference", "value": "private-value"}, csrf) post(client, "/api/v1/users", {"username": role, "password": PASSWORD, "role": role}, csrf) user_csrf = login(client, username=role) response = client.get("/api/v1/secrets") assert response.status_code == metadata_status if metadata_status == 200: assert response.json()[0]["id"] == created["id"] assert set(response.json()[0]) == {"id", "name", "created_at"} assert "private-value" not in response.text denied = client.post("/api/v1/secrets", json={ "name": "Denied password", "kind": "root_password", "value": "Forbidden-password-2026!", }, headers=user_csrf) assert denied.status_code == 403 assert "Forbidden-password" not in denied.text def test_installer_password_hash_uses_random_salt(): password = "Installer-password-2026!" first = Security.hash_installer_password(password) second = Security.hash_installer_password(password) assert first != second assert sha512_crypt.verify(password, first) assert sha512_crypt.verify(password, second)