diff --git a/src/main/java/com/wallet/secure/audit/entity/AuditLog.java b/src/main/java/com/wallet/secure/audit/entity/AuditLog.java index 84e8d8f..0547ad8 100644 --- a/src/main/java/com/wallet/secure/audit/entity/AuditLog.java +++ b/src/main/java/com/wallet/secure/audit/entity/AuditLog.java @@ -108,6 +108,7 @@ public class AuditLog { * Supports both IPv4 and IPv6. * OWASP A09: essential for geographic anomaly detection and forensics. */ + @JdbcTypeCode(SqlTypes.INET) @Column(name = "ip_address") private String ipAddress; diff --git a/src/main/java/com/wallet/secure/auth/entity/Session.java b/src/main/java/com/wallet/secure/auth/entity/Session.java index cc938d8..1eaef65 100644 --- a/src/main/java/com/wallet/secure/auth/entity/Session.java +++ b/src/main/java/com/wallet/secure/auth/entity/Session.java @@ -3,6 +3,8 @@ import com.wallet.secure.user.entity.User; import jakarta.persistence.*; import lombok.*; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; @@ -87,6 +89,7 @@ public class Session { * OWASP A07: enables geographic anomaly detection. * Example: session created in Colombia, refresh from Russia → suspicious. */ + @JdbcTypeCode(SqlTypes.INET) @Column(name = "ip_address") private String ipAddress; diff --git a/src/main/java/com/wallet/secure/config/AdminBootstrapProperties.java b/src/main/java/com/wallet/secure/config/AdminBootstrapProperties.java index 9ccee42..163d6e0 100644 --- a/src/main/java/com/wallet/secure/config/AdminBootstrapProperties.java +++ b/src/main/java/com/wallet/secure/config/AdminBootstrapProperties.java @@ -30,7 +30,8 @@ public class AdminBootstrapProperties { @NotBlank(message = "Contraseña obligatoria") @Size(min = 12, max = 72, message = "La contraseña debe tener entre 12 y 72 caracteres") @Pattern( - regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&_.#^])[A-Za-z\\d@$!%*?&_.#^]{12,72}$", + // Do not whitelist symbols: passwords from password managers may contain /, +, =, etc. + regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[^A-Za-z0-9\\s])\\S{12,72}$", message = "La contraseña debe incluir mayúscula, minúscula, número y carácter especial" ) private String password; diff --git a/src/main/java/com/wallet/secure/user/dto/RegisterRequest.java b/src/main/java/com/wallet/secure/user/dto/RegisterRequest.java index 9f7962b..81fa36f 100644 --- a/src/main/java/com/wallet/secure/user/dto/RegisterRequest.java +++ b/src/main/java/com/wallet/secure/user/dto/RegisterRequest.java @@ -33,7 +33,8 @@ public class RegisterRequest { @NotBlank(message = "Password is required") @Size(min = 12, max = 72, message = "Password must be between 12 and 72 characters") @Pattern( - regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&_.#^])[A-Za-z\\d@$!%*?&_.#^]{12,}$", + // OWASP: do not restrict the symbols a password manager may generate. + regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[^A-Za-z0-9\\s])\\S{12,72}$", message = "Password must contain uppercase, lowercase, number and special character" ) private String password; diff --git a/src/main/java/com/wallet/secure/wallet/entity/Wallet.java b/src/main/java/com/wallet/secure/wallet/entity/Wallet.java index 8be3e24..5bb5d77 100644 --- a/src/main/java/com/wallet/secure/wallet/entity/Wallet.java +++ b/src/main/java/com/wallet/secure/wallet/entity/Wallet.java @@ -104,7 +104,7 @@ public class Wallet { private Instant createdAt; @LastModifiedDate - @Column(name = "update_at", nullable = false) + @Column(name = "updated_at", nullable = false) private Instant updateAt; // --- Business Methods diff --git a/src/test/java/com/wallet/secure/user/service/AdminBootstrapServiceTest.java b/src/test/java/com/wallet/secure/user/service/AdminBootstrapServiceTest.java index 8a09715..47378e9 100644 --- a/src/test/java/com/wallet/secure/user/service/AdminBootstrapServiceTest.java +++ b/src/test/java/com/wallet/secure/user/service/AdminBootstrapServiceTest.java @@ -60,10 +60,10 @@ void disabledBootstrapDoesNotQueryOrCreateUser() throws Exception { void validBootstrapCreatesVerifiedAdminWithEncodedPassword() throws Exception { properties.setEnabled(true); properties.setEmail("admin@example.test"); - properties.setPassword("StrongPass1!"); + properties.setPassword("Strong/Pass1!"); when(userRepository.existsByRole(UserRole.ADMIN)).thenReturn(false); when(userRepository.existsByEmail("admin@example.test")).thenReturn(false); - when(passwordEncoder.encode("StrongPass1!")).thenReturn("$2a$12$encoded"); + when(passwordEncoder.encode("Strong/Pass1!")).thenReturn("$2a$12$encoded"); service.run(new DefaultApplicationArguments()); @@ -74,7 +74,7 @@ void validBootstrapCreatesVerifiedAdminWithEncodedPassword() throws Exception { assertThat(saved.getEmail()).isEqualTo("admin@example.test"); assertThat(saved.getPasswordHash()).isEqualTo("$2a$12$encoded"); assertThat(saved.getEmailVerified()).isTrue(); - verify(passwordEncoder).encode("StrongPass1!"); + verify(passwordEncoder).encode("Strong/Pass1!"); } @Test @@ -82,7 +82,7 @@ void validBootstrapCreatesVerifiedAdminWithEncodedPassword() throws Exception { void existingAdminPreventsSecondAdmin() throws Exception { properties.setEnabled(true); properties.setEmail("admin@example.test"); - properties.setPassword("StrongPass1!"); + properties.setPassword("Strong/Pass1!"); when(userRepository.existsByRole(UserRole.ADMIN)).thenReturn(true); service.run(new DefaultApplicationArguments());