diff --git a/common/src/main/java/bisq/common/util/ExtraDataMapValidator.java b/common/src/main/java/bisq/common/util/ExtraDataMapValidator.java
new file mode 100644
index 0000000000..d0f10bc5ba
--- /dev/null
+++ b/common/src/main/java/bisq/common/util/ExtraDataMapValidator.java
@@ -0,0 +1,75 @@
+/*
+ * This file is part of Bisq.
+ *
+ * Bisq is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bisq is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bisq. If not, see .
+ */
+
+package bisq.common.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import lombok.extern.slf4j.Slf4j;
+
+import javax.annotation.Nullable;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Validator for extraDataMap fields used in network payloads.
+ * Ensures that we don't get the network attacked by huge data inserted there.
+ */
+@Slf4j
+public class ExtraDataMapValidator {
+ // ExtraDataMap is only used for exceptional cases to not break backward compatibility.
+ // We don't expect many entries there.
+ public final static int MAX_SIZE = 10;
+ public final static int MAX_KEY_LENGTH = 100;
+ public final static int MAX_VALUE_LENGTH = 100000; // 100 kb
+
+ public static Map getValidatedExtraDataMap(@Nullable Map extraDataMap) {
+ return getValidatedExtraDataMap(extraDataMap, MAX_SIZE, MAX_KEY_LENGTH, MAX_VALUE_LENGTH);
+ }
+
+ public static Map getValidatedExtraDataMap(@Nullable Map extraDataMap, int maxSize, int maxKeyLength, int maxValueLength) {
+ if (extraDataMap == null)
+ return null;
+
+ try {
+ checkArgument(extraDataMap.entrySet().size() <= maxSize, "Size of map must not exceed " + maxSize);
+ extraDataMap.forEach((key, value) -> {
+ checkArgument(key.length() <= maxKeyLength, "Length of key must not exceed " + maxKeyLength);
+ checkArgument(value.length() <= maxValueLength, "Length of value must not exceed " + maxValueLength);
+ });
+ return extraDataMap;
+ } catch (Throwable t) {
+ return new HashMap<>();
+ }
+ }
+
+ public static void validate(@Nullable Map extraDataMap) {
+ validate(extraDataMap, MAX_SIZE, MAX_KEY_LENGTH, MAX_VALUE_LENGTH);
+ }
+
+ public static void validate(@Nullable Map extraDataMap, int maxSize, int maxKeyLength, int maxValueLength) {
+ if (extraDataMap == null)
+ return;
+
+ checkArgument(extraDataMap.entrySet().size() <= maxSize, "Size of map must not exceed " + maxSize);
+ extraDataMap.forEach((key, value) -> {
+ checkArgument(key.length() <= maxKeyLength, "Length of key must not exceed " + maxKeyLength);
+ checkArgument(value.length() <= maxValueLength, "Length of value must not exceed " + maxValueLength);
+ });
+ }
+}
diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteValidator.java b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteValidator.java
index e16542887f..4b1035a25e 100644
--- a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteValidator.java
+++ b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteValidator.java
@@ -17,12 +17,15 @@
package bisq.core.dao.governance.blindvote;
+import bisq.core.btc.wallet.Restrictions;
import bisq.core.dao.governance.period.PeriodService;
import bisq.core.dao.governance.proposal.ProposalValidationException;
import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.Tx;
import bisq.core.dao.state.model.governance.DaoPhase;
+import bisq.common.util.ExtraDataMapValidator;
+
import javax.inject.Inject;
import java.util.Optional;
@@ -58,12 +61,20 @@ public class BlindVoteValidator {
checkNotNull(blindVote.getEncryptedVotes(), "encryptedProposalList must not be null");
checkArgument(blindVote.getEncryptedVotes().length > 0,
"encryptedProposalList must not be empty");
- checkNotNull(blindVote.getTxId(), "txId must not be null");
- checkArgument(!blindVote.getTxId().isEmpty(), "txId must not be empty");
- checkArgument(blindVote.getStake() > 0, "stake must be positive");
+ checkArgument(blindVote.getEncryptedVotes().length <= 100000,
+ "encryptedProposalList must not exceed 100kb");
+
+ checkNotNull(blindVote.getTxId(), "Tx ID must not be null");
+ checkArgument(blindVote.getTxId().length() == 64, "Tx ID must be 64 chars");
+ checkArgument(blindVote.getStake() >= Restrictions.getMinNonDustOutput().value, "Stake must be at least MinNonDustOutput");
+
checkNotNull(blindVote.getEncryptedMeritList(), "getEncryptedMeritList must not be null");
checkArgument(blindVote.getEncryptedMeritList().length > 0,
"getEncryptedMeritList must not be empty");
+ checkArgument(blindVote.getEncryptedMeritList().length <= 100000,
+ "getEncryptedMeritList must not exceed 100kb");
+
+ ExtraDataMapValidator.validate(blindVote.getExtraDataMap());
} catch (Throwable e) {
log.warn(e.toString());
throw new ProposalValidationException(e);
diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java
index d303246d94..e0f0118b1c 100644
--- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java
+++ b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java
@@ -28,6 +28,8 @@ import bisq.core.dao.state.model.governance.DaoPhase;
import bisq.core.dao.state.model.governance.Proposal;
import bisq.core.dao.state.model.governance.ReimbursementProposal;
+import bisq.common.util.ExtraDataMapValidator;
+
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
@@ -66,6 +68,8 @@ public abstract class ProposalValidator implements ConsensusCritical {
checkArgument(proposal.getLink().length() <= 200, "Link must not exceed 200 chars");
if (proposal.getTxId() != null)
checkArgument(proposal.getTxId().length() == 64, "Tx ID must be 64 chars");
+
+ ExtraDataMapValidator.validate(proposal.getExtraDataMap());
} catch (Throwable throwable) {
throw new ProposalValidationException(throwable);
}
diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalPayload.java b/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalPayload.java
index 8ba1639092..38b993f1e6 100644
--- a/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalPayload.java
+++ b/core/src/main/java/bisq/core/dao/governance/proposal/storage/temp/TempProposalPayload.java
@@ -28,6 +28,7 @@ import bisq.common.app.Capabilities;
import bisq.common.app.Capability;
import bisq.common.crypto.Sig;
import bisq.common.proto.persistable.PersistablePayload;
+import bisq.common.util.ExtraDataMapValidator;
import io.bisq.generated.protobuffer.PB;
@@ -88,7 +89,7 @@ public class TempProposalPayload implements LazyProcessedPayload, ProtectedStora
@Nullable Map extraDataMap) {
this.proposal = proposal;
this.ownerPubKeyEncoded = ownerPubPubKeyEncoded;
- this.extraDataMap = extraDataMap;
+ this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap);
ownerPubKey = Sig.getPublicKeyFromBytes(ownerPubKeyEncoded);
}
diff --git a/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java b/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java
index fd53adc226..395b29c959 100644
--- a/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java
+++ b/core/src/main/java/bisq/core/dao/state/model/governance/Proposal.java
@@ -26,6 +26,7 @@ import bisq.core.dao.state.model.blockchain.TxType;
import bisq.common.proto.ProtobufferRuntimeException;
import bisq.common.proto.network.NetworkPayload;
import bisq.common.proto.persistable.PersistablePayload;
+import bisq.common.util.ExtraDataMapValidator;
import io.bisq.generated.protobuffer.PB;
@@ -64,13 +65,13 @@ public abstract class Proposal implements PersistablePayload, NetworkPayload, Co
byte version,
long creationDate,
@Nullable String txId,
- @SuppressWarnings("NullableProblems") Map extraDataMap) {
+ @Nullable Map extraDataMap) {
this.name = name;
this.link = link;
this.version = version;
this.creationDate = creationDate;
this.txId = txId;
- this.extraDataMap = extraDataMap;
+ this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap);
}