fix(security): lock down signing-key file permissions

Private JWK files were written under the default umask (observed 0664 — group
and world readable). Create the key directory 0700, chmod private key files
(private_jwks.json, token_jwks.json) to 0600 after they are written, and
refuse to start if a pre-existing private key is group/world accessible.

Tests now use an isolated per-test key directory.

Refs: porchlight-91i

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Johan Lundberg 2026-06-08 15:21:27 +02:00
parent cba63280fb
commit c7550cbf09
No known key found for this signature in database
GPG key ID: A6C152738D03C7D1
4 changed files with 67 additions and 6 deletions

View file

@ -19,7 +19,7 @@ scope = ["openid", "profile"]
toml_file = tmp_path / "test.toml"
toml_file.write_text(toml_content)
settings = Settings(_toml_file=str(toml_file), session_secret="x" * 32)
settings = Settings(_toml_file=str(toml_file), session_secret="x" * 32, signing_key_path=str(tmp_path / "keys"))
app = create_app(settings)
async with (
@ -39,9 +39,11 @@ scope = ["openid", "profile"]
assert cdb_entry["allowed_scopes"] == ["openid", "profile"]
async def test_manage_app_always_registered() -> None:
async def test_manage_app_always_registered(tmp_path: Path) -> None:
"""The internal manage-app client is always registered, even without config file clients."""
settings = Settings(issuer="https://test.example.com", session_secret="x" * 32)
settings = Settings(
issuer="https://test.example.com", session_secret="x" * 32, signing_key_path=str(tmp_path / "keys")
)
app = create_app(settings)
async with (