(bugfix) Normalize pasted OpenSSH private keys

Canonicalize uploaded OpenSSH private keys before validation by
normalizing line endings, removing whitespace from the base64 body,
and re-wrapping it between the BEGIN and END markers.

Add SSH credential tests that generate a real ed25519 key, damage its
wrapping, and verify that validation succeeds after normalization.

Return a clearer validation error for PEM private keys, which are not
supported by the current credential flow.
This commit is contained in:
2026-05-19 18:42:02 +02:00
parent c7cfb603b0
commit 97797c574d
2 changed files with 61 additions and 2 deletions

View File

@@ -0,0 +1,40 @@
from __future__ import annotations
import subprocess
from pathlib import Path
from tempfile import TemporaryDirectory
from django import forms
from django.test import SimpleTestCase
from pobsync_backend.forms import normalize_private_key, validate_ssh_private_key
class SshCredentialValidationTests(SimpleTestCase):
def test_normalize_private_key_repairs_wrapped_openssh_body(self) -> None:
with TemporaryDirectory() as tmp:
key_path = Path(tmp) / "identity"
subprocess.run(
["ssh-keygen", "-t", "ed25519", "-N", "", "-C", "test", "-f", str(key_path)],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
private_key = key_path.read_text(encoding="utf-8")
begin_marker = "-----BEGIN OPENSSH PRIVATE KEY-----"
end_marker = "-----END OPENSSH PRIVATE KEY-----"
body = private_key.split(begin_marker, 1)[1].split(end_marker, 1)[0]
damaged_body = " \n ".join(body.split())
damaged_key = f"{begin_marker}\n{damaged_body}\n{end_marker}"
normalized_key = normalize_private_key(damaged_key)
self.assertEqual(validate_ssh_private_key(normalized_key), validate_ssh_private_key(private_key))
def test_validate_private_key_rejects_pem_key_with_actionable_message(self) -> None:
with self.assertRaises(forms.ValidationError) as exc:
validate_ssh_private_key("-----BEGIN RSA PRIVATE KEY-----\nabc\n-----END RSA PRIVATE KEY-----")
self.assertIn("PEM private keys are not supported", str(exc.exception))