diff --git a/backend/handlers/auth/auth_test.go b/backend/handlers/auth/auth_test.go
index cc71ab1..47db4eb 100644
--- a/backend/handlers/auth/auth_test.go
+++ b/backend/handlers/auth/auth_test.go
@@ -1469,5 +1469,59 @@ func TestLoginInProgress_Cap(t *testing.T) {
}
}
+// ValidateUKPhoneNumber Security Tests
+//
+// These tests verify that ValidateUKPhoneNumber rejects or sanitises
+// injection payloads (SQLi, XSS, command injection, control characters).
+
+func TestValidateUKPhoneNumber_RejectsPureInjectionPayloads(t *testing.T) {
+ payloads := []string{
+ // SQL injection
+ "' OR '1'='1",
+ "admin'--",
+ `" OR 1=1 --`,
+ "'; DROP TABLE users;--",
+ // XSS
+ "",
+ "
",
+ "\">",
+ "javascript:alert(1)",
+ // Command injection
+ "; rm -rf /",
+ "| cat /etc/passwd",
+ "`id`",
+ "$(cat /etc/passwd)",
+ // Control characters
+ "\n",
+ "\r\n",
+ "\x00",
+ }
+ for _, p := range payloads {
+ result, err := ValidateUKPhoneNumber(p)
+ if err == nil {
+ t.Errorf("expected injection payload %q to be rejected, got result %q", p, result)
+ }
+ }
+}
+
+func TestValidateUKPhoneNumber_RejectsMixedInjectionPayloads(t *testing.T) {
+ // When injection characters are interleaved with a valid UK phone number,
+ // libphonenumber rejects the entire input — it does NOT try to extract
+ // digits from non-numeric characters. This is MORE secure than naive
+ // digit-stripping approaches.
+ payloads := []string{
+ "' OR '1'='1 OR '+447700900000",
+ ">",
+ "'; rm -rf /; +447700900000",
+ "\x00\n+447700900000",
+ }
+ for _, p := range payloads {
+ result, err := ValidateUKPhoneNumber(p)
+ if err == nil {
+ t.Errorf("expected mixed payload %q to be rejected, got result %q", p, result)
+ }
+ }
+}
+
// Ensure test compilation - import pgxpool to avoid unused import
var _ = func() *pgxpool.Pool { return nil }