Add more validation

- Check max length of strings and byte arrays
- Check that tx ID has 64 chars
- Add ExtraDataMapValidator for validating extraDataMap fields
This commit is contained in:
Manfred Karrer 2019-03-31 15:49:37 -05:00
parent 122bc80cdd
commit 73db81a34f
No known key found for this signature in database
GPG key ID: 401250966A6B2C46
5 changed files with 98 additions and 6 deletions

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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<String, String> getValidatedExtraDataMap(@Nullable Map<String, String> extraDataMap) {
return getValidatedExtraDataMap(extraDataMap, MAX_SIZE, MAX_KEY_LENGTH, MAX_VALUE_LENGTH);
}
public static Map<String, String> getValidatedExtraDataMap(@Nullable Map<String, String> 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<String, String> extraDataMap) {
validate(extraDataMap, MAX_SIZE, MAX_KEY_LENGTH, MAX_VALUE_LENGTH);
}
public static void validate(@Nullable Map<String, String> 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);
});
}
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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<String, String> extraDataMap) {
this.proposal = proposal;
this.ownerPubKeyEncoded = ownerPubPubKeyEncoded;
this.extraDataMap = extraDataMap;
this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap);
ownerPubKey = Sig.getPublicKeyFromBytes(ownerPubKeyEncoded);
}

View file

@ -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<String, String> extraDataMap) {
@Nullable Map<String, String> extraDataMap) {
this.name = name;
this.link = link;
this.version = version;
this.creationDate = creationDate;
this.txId = txId;
this.extraDataMap = extraDataMap;
this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap);
}