Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions sdk-generation-log/configurationwebhooks.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"service": "configurationwebhooks",
"project": "java",
"generatedAt": "2026-06-02T15:30:09Z",
"openapiCommitSha": "f3894fd6b47d9076841b04852378251de3bba2de",
"automationCommitSha": "6f06b47d0661f0891defe6b85461d2c367fbd284",
"libraryCommitSha": "9539e092f2160ca341916c804f01f0f699a64415"
"generatedAt": "2026-07-08T18:09:51Z",
"openapiCommitSha": "334900842b135eb9fc1a17afebd46a4f07793aec",
"automationCommitSha": "5f1d5ab9643886ef722a2626e23afef8db5d52a6",
"libraryCommitSha": "9272fbb8dc027b507a9e88aca0a36557e80e6e52"
}
41 changes: 21 additions & 20 deletions src/main/java/com/adyen/model/configurationwebhooks/Mandate.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.Arrays;
import java.util.logging.Logger;
Expand All @@ -40,7 +41,7 @@ public class Mandate {
private MandateBankAccount counterparty;

public static final String JSON_PROPERTY_CREATED_AT = "createdAt";
private Object createdAt;
private OffsetDateTime createdAt;

public static final String JSON_PROPERTY_ID = "id";
private String id;
Expand Down Expand Up @@ -137,7 +138,7 @@ public static TypeEnum fromValue(String value) {
private TypeEnum type;

public static final String JSON_PROPERTY_UPDATED_AT = "updatedAt";
private Object updatedAt;
private OffsetDateTime updatedAt;

public Mandate() {}

Expand Down Expand Up @@ -211,35 +212,35 @@ public void setCounterparty(MandateBankAccount counterparty) {
}

/**
* createdAt
* The date when the mandate was created.
*
* @param createdAt
* @param createdAt The date when the mandate was created.
* @return the current {@code Mandate} instance, allowing for method chaining
*/
public Mandate createdAt(Object createdAt) {
public Mandate createdAt(OffsetDateTime createdAt) {
this.createdAt = createdAt;
return this;
}

/**
* Get createdAt
* The date when the mandate was created.
*
* @return createdAt
* @return createdAt The date when the mandate was created.
*/
@JsonProperty(JSON_PROPERTY_CREATED_AT)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
public Object getCreatedAt() {
public OffsetDateTime getCreatedAt() {
return createdAt;
}

/**
* createdAt
* The date when the mandate was created.
*
* @param createdAt
* @param createdAt The date when the mandate was created.
*/
@JsonProperty(JSON_PROPERTY_CREATED_AT)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
public void setCreatedAt(Object createdAt) {
public void setCreatedAt(OffsetDateTime createdAt) {
this.createdAt = createdAt;
}

Expand Down Expand Up @@ -382,35 +383,35 @@ public void setType(TypeEnum type) {
}

/**
* updatedAt
* The date when the mandate was updated.
*
* @param updatedAt
* @param updatedAt The date when the mandate was updated.
* @return the current {@code Mandate} instance, allowing for method chaining
*/
public Mandate updatedAt(Object updatedAt) {
public Mandate updatedAt(OffsetDateTime updatedAt) {
this.updatedAt = updatedAt;
return this;
}

/**
* Get updatedAt
* The date when the mandate was updated.
*
* @return updatedAt
* @return updatedAt The date when the mandate was updated.
*/
@JsonProperty(JSON_PROPERTY_UPDATED_AT)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
public Object getUpdatedAt() {
public OffsetDateTime getUpdatedAt() {
return updatedAt;
}

/**
* updatedAt
* The date when the mandate was updated.
*
* @param updatedAt
* @param updatedAt The date when the mandate was updated.
*/
@JsonProperty(JSON_PROPERTY_UPDATED_AT)
@JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
public void setUpdatedAt(Object updatedAt) {
public void setUpdatedAt(OffsetDateTime updatedAt) {
this.updatedAt = updatedAt;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,13 @@ public MandateBankAccountAccountIdentification(UKLocalMandateAccountIdentificati
new GenericType<UKLocalMandateAccountIdentification>() {});
JSON.registerDescendants(
MandateBankAccountAccountIdentification.class, Collections.unmodifiableMap(schemas));
// Initialize and register the discriminator mappings.
Map<String, Class<?>> mappings = new HashMap<>();
mappings.put("ukLocal", UKLocalMandateAccountIdentification.class);
mappings.put("UKLocalMandateAccountIdentification", UKLocalMandateAccountIdentification.class);
mappings.put(
"MandateBankAccount_accountIdentification", MandateBankAccountAccountIdentification.class);
JSON.registerDiscriminator(MandateBankAccountAccountIdentification.class, "type", mappings);
Comment on lines +155 to +161

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

1. Redundant Self-Mapping in Discriminator (Medium)

The mapping mappings.put("MandateBankAccount_accountIdentification", MandateBankAccountAccountIdentification.class); maps the parent/composed class to itself. A discriminator should only map to concrete subtype classes (like UKLocalMandateAccountIdentification). If this mapping is ever matched, the custom deserializer MandateBankAccountAccountIdentificationDeserializer will fail anyway because "MandateBankAccount_accountIdentification" is not a valid enum value for any of the subtypes. Removing this self-mapping keeps the code clean and avoids confusion.

2. Concurrency Vulnerability in JSON.registerDiscriminator (High)

The static block calls JSON.registerDiscriminator(...). Under the hood, JSON.java stores these mappings in a plain HashMap (modelDiscriminators). Since different model classes can be loaded concurrently on different threads (especially in a multi-threaded webhook processing environment), concurrent writes to this HashMap can cause race conditions, data corruption, or infinite loops.

Recommendation:
Although JSON.java is not modified in this PR, it should be updated to use ConcurrentHashMap for both modelDiscriminators and modelDescendants to ensure thread safety during class loading:

private static final Map<Class<?>, ClassDiscriminatorMapping> modelDiscriminators =
    new ConcurrentHashMap<>();
private static final Map<Class<?>, Map<String, GenericType>> modelDescendants =
    new ConcurrentHashMap<>();
    // Initialize and register the discriminator mappings.
    Map<String, Class<?>> mappings = new HashMap<>();
    mappings.put("ukLocal", UKLocalMandateAccountIdentification.class);
    mappings.put("UKLocalMandateAccountIdentification", UKLocalMandateAccountIdentification.class);
    JSON.registerDiscriminator(MandateBankAccountAccountIdentification.class, "type", mappings);

}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,15 @@ public PaymentInstrumentAdditionalBankAccountIdentificationsInner(IbanAccountIde
JSON.registerDescendants(
PaymentInstrumentAdditionalBankAccountIdentificationsInner.class,
Collections.unmodifiableMap(schemas));
// Initialize and register the discriminator mappings.
Map<String, Class<?>> mappings = new HashMap<>();
mappings.put("iban", IbanAccountIdentification.class);
mappings.put("IbanAccountIdentification", IbanAccountIdentification.class);
mappings.put(
"PaymentInstrument_additionalBankAccountIdentifications_inner",
PaymentInstrumentAdditionalBankAccountIdentificationsInner.class);
JSON.registerDiscriminator(
PaymentInstrumentAdditionalBankAccountIdentificationsInner.class, "type", mappings);
Comment on lines +154 to +162

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

1. Redundant Self-Mapping in Discriminator (Medium)

The mapping mappings.put("PaymentInstrument_additionalBankAccountIdentifications_inner", PaymentInstrumentAdditionalBankAccountIdentificationsInner.class); maps the parent/composed class to itself. A discriminator should only map to concrete subtype classes (like IbanAccountIdentification). If this mapping is ever matched, the custom deserializer PaymentInstrumentAdditionalBankAccountIdentificationsInnerDeserializer will fail anyway because "PaymentInstrument_additionalBankAccountIdentifications_inner" is not a valid enum value for any of the subtypes. Removing this self-mapping keeps the code clean and avoids confusion.

2. Concurrency Vulnerability in JSON.registerDiscriminator (High)

The static block calls JSON.registerDiscriminator(...). Under the hood, JSON.java stores these mappings in a plain HashMap (modelDiscriminators). Since different model classes can be loaded concurrently on different threads (especially in a multi-threaded webhook processing environment), concurrent writes to this HashMap can cause race conditions, data corruption, or infinite loops.

Recommendation:
Although JSON.java is not modified in this PR, it should be updated to use ConcurrentHashMap for both modelDiscriminators and modelDescendants to ensure thread safety during class loading:

private static final Map<Class<?>, ClassDiscriminatorMapping> modelDiscriminators =
    new ConcurrentHashMap<>();
private static final Map<Class<?>, Map<String, GenericType>> modelDescendants =
    new ConcurrentHashMap<>();
    // Initialize and register the discriminator mappings.
    Map<String, Class<?>> mappings = new HashMap<>();
    mappings.put("iban", IbanAccountIdentification.class);
    mappings.put("IbanAccountIdentification", IbanAccountIdentification.class);
    JSON.registerDiscriminator(
        PaymentInstrumentAdditionalBankAccountIdentificationsInner.class, "type", mappings);

}

@Override
Expand Down