mirror of
https://github.com/AsamK/signal-cli.git
synced 2026-09-20 06:09:21 +00:00
Handle invalid range variants safely
This commit is contained in:
parent
a770b03fe6
commit
8b74f0653d
@ -73,6 +73,7 @@ import org.whispersystems.signalservice.api.push.ServiceIdType;
|
|||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.internal.push.BodyRange;
|
import org.whispersystems.signalservice.internal.push.BodyRange;
|
||||||
import org.whispersystems.signalservice.internal.push.Content;
|
import org.whispersystems.signalservice.internal.push.Content;
|
||||||
|
import org.whispersystems.signalservice.internal.push.DataMessage;
|
||||||
import org.whispersystems.signalservice.internal.push.Envelope;
|
import org.whispersystems.signalservice.internal.push.Envelope;
|
||||||
import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException;
|
import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException;
|
||||||
|
|
||||||
@ -89,6 +90,12 @@ import java.util.stream.Collectors;
|
|||||||
public final class IncomingMessageHandler {
|
public final class IncomingMessageHandler {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(IncomingMessageHandler.class);
|
private static final Logger logger = LoggerFactory.getLogger(IncomingMessageHandler.class);
|
||||||
|
private static final String DATA_MESSAGE_BODY_RANGE_OUT_OF_BOUNDS_REASON =
|
||||||
|
"[DataMessage] Body range with out-of-bounds start/length!";
|
||||||
|
private static final String DATA_MESSAGE_QUOTE_BODY_RANGE_OUT_OF_BOUNDS_REASON =
|
||||||
|
"[DataMessage] Quote body range with out-of-bounds start/length!";
|
||||||
|
private static final String EDIT_MESSAGE_BODY_RANGE_OUT_OF_BOUNDS_REASON =
|
||||||
|
"[EditMessage] Body range with out-of-bounds start/length!";
|
||||||
|
|
||||||
private final SignalAccount account;
|
private final SignalAccount account;
|
||||||
private final SignalDependencies dependencies;
|
private final SignalDependencies dependencies;
|
||||||
@ -271,21 +278,31 @@ public final class IncomingMessageHandler {
|
|||||||
final EnvelopeMetadata envelopeMetadata,
|
final EnvelopeMetadata envelopeMetadata,
|
||||||
final Content content
|
final Content content
|
||||||
) {
|
) {
|
||||||
final var dataMessage = content.dataMessage != null
|
final var reason = validationResult.getReason();
|
||||||
? content.dataMessage
|
final var dataMessage = getDataMessage(content, reason);
|
||||||
: content.syncMessage != null && content.syncMessage.sent != null
|
final String body;
|
||||||
? content.syncMessage.sent.message
|
final List<BodyRange> bodyRanges;
|
||||||
: null;
|
if (DATA_MESSAGE_QUOTE_BODY_RANGE_OUT_OF_BOUNDS_REASON.equals(reason)) {
|
||||||
final Integer bodyLength = dataMessage == null
|
if (dataMessage == null || dataMessage.quote == null) {
|
||||||
? null
|
body = null;
|
||||||
: dataMessage.body == null ? 0 : dataMessage.body.length();
|
bodyRanges = null;
|
||||||
final List<InvalidEnvelopeContentException.InvalidBodyRange> invalidBodyRanges;
|
} else {
|
||||||
if (dataMessage == null) {
|
body = dataMessage.quote.text;
|
||||||
invalidBodyRanges = List.of();
|
bodyRanges = dataMessage.quote.bodyRanges;
|
||||||
|
}
|
||||||
|
} else if (dataMessage == null) {
|
||||||
|
body = null;
|
||||||
|
bodyRanges = null;
|
||||||
} else {
|
} else {
|
||||||
invalidBodyRanges = new ArrayList<>();
|
body = dataMessage.body;
|
||||||
for (int i = 0; i < dataMessage.bodyRanges.size(); i++) {
|
bodyRanges = dataMessage.bodyRanges;
|
||||||
final var range = dataMessage.bodyRanges.get(i);
|
}
|
||||||
|
|
||||||
|
final Integer bodyLength = bodyRanges == null ? null : body == null ? 0 : body.length();
|
||||||
|
final List<InvalidEnvelopeContentException.InvalidBodyRange> invalidBodyRanges = new ArrayList<>();
|
||||||
|
if (bodyRanges != null) {
|
||||||
|
for (int i = 0; i < bodyRanges.size(); i++) {
|
||||||
|
final var range = bodyRanges.get(i);
|
||||||
final long start = range.start == null ? 0 : range.start;
|
final long start = range.start == null ? 0 : range.start;
|
||||||
final long length = range.length == null ? 0 : range.length;
|
final long length = range.length == null ? 0 : range.length;
|
||||||
if (start < 0 || length < 0 || start + length > bodyLength) {
|
if (start < 0 || length < 0 || start + length > bodyLength) {
|
||||||
@ -297,10 +314,10 @@ public final class IncomingMessageHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final var code = "[DataMessage] Body range with out-of-bounds start/length!".equals(validationResult.getReason())
|
final var code = isBodyRangeOutOfBoundsReason(reason)
|
||||||
? InvalidEnvelopeContentException.DATA_MESSAGE_BODY_RANGE_OUT_OF_BOUNDS
|
? InvalidEnvelopeContentException.DATA_MESSAGE_BODY_RANGE_OUT_OF_BOUNDS
|
||||||
: InvalidEnvelopeContentException.INVALID_ENVELOPE_CONTENT;
|
: InvalidEnvelopeContentException.INVALID_ENVELOPE_CONTENT;
|
||||||
return new InvalidEnvelopeContentException(validationResult.getReason(),
|
return new InvalidEnvelopeContentException(reason,
|
||||||
code,
|
code,
|
||||||
envelopeMetadata.getSourceServiceId().toString(),
|
envelopeMetadata.getSourceServiceId().toString(),
|
||||||
envelopeMetadata.getSourceDeviceId(),
|
envelopeMetadata.getSourceDeviceId(),
|
||||||
@ -309,6 +326,38 @@ public final class IncomingMessageHandler {
|
|||||||
validationResult.getThrowable());
|
validationResult.getThrowable());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static DataMessage getDataMessage(final Content content, final String validationReason) {
|
||||||
|
if (validationReason.startsWith("[EditMessage]")) {
|
||||||
|
return getEditDataMessage(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.dataMessage != null) {
|
||||||
|
return content.dataMessage;
|
||||||
|
}
|
||||||
|
if (content.syncMessage != null && content.syncMessage.sent != null
|
||||||
|
&& content.syncMessage.sent.message != null) {
|
||||||
|
return content.syncMessage.sent.message;
|
||||||
|
}
|
||||||
|
return getEditDataMessage(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DataMessage getEditDataMessage(final Content content) {
|
||||||
|
if (content.editMessage != null) {
|
||||||
|
return content.editMessage.dataMessage;
|
||||||
|
}
|
||||||
|
if (content.syncMessage != null && content.syncMessage.sent != null
|
||||||
|
&& content.syncMessage.sent.editMessage != null) {
|
||||||
|
return content.syncMessage.sent.editMessage.dataMessage;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBodyRangeOutOfBoundsReason(final String reason) {
|
||||||
|
return DATA_MESSAGE_BODY_RANGE_OUT_OF_BOUNDS_REASON.equals(reason)
|
||||||
|
|| DATA_MESSAGE_QUOTE_BODY_RANGE_OUT_OF_BOUNDS_REASON.equals(reason)
|
||||||
|
|| EDIT_MESSAGE_BODY_RANGE_OUT_OF_BOUNDS_REASON.equals(reason);
|
||||||
|
}
|
||||||
|
|
||||||
private static String getBodyRangeType(final BodyRange range) {
|
private static String getBodyRangeType(final BodyRange range) {
|
||||||
if (range.style != null) {
|
if (range.style != null) {
|
||||||
return "STYLE_" + range.style.name();
|
return "STYLE_" + range.style.name();
|
||||||
@ -357,7 +406,7 @@ public final class IncomingMessageHandler {
|
|||||||
// Check again in case the user just joined the group
|
// Check again in case the user just joined the group
|
||||||
notAllowedToSendToGroup = notAllowedToSendToGroup && isNotAllowedToSendToGroup(envelope, content);
|
notAllowedToSendToGroup = notAllowedToSendToGroup && isNotAllowedToSendToGroup(envelope, content);
|
||||||
|
|
||||||
if (isMessageBlocked(envelope, content)) {
|
if (isMessageBlocked(envelope, content, exception)) {
|
||||||
logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp());
|
logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp());
|
||||||
return List.of();
|
return List.of();
|
||||||
} else if (notAllowedToSendToGroup) {
|
} else if (notAllowedToSendToGroup) {
|
||||||
@ -826,8 +875,12 @@ public final class IncomingMessageHandler {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isMessageBlocked(SignalServiceEnvelope envelope, SignalServiceContent content) {
|
private boolean isMessageBlocked(
|
||||||
SignalServiceAddress source = getSenderAddress(envelope, content);
|
SignalServiceEnvelope envelope,
|
||||||
|
SignalServiceContent content,
|
||||||
|
Exception exception
|
||||||
|
) {
|
||||||
|
SignalServiceAddress source = getSenderAddress(envelope, content, exception);
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1134,7 +1187,10 @@ public final class IncomingMessageHandler {
|
|||||||
this.account.getProfileStore().storeProfileKey(source, profileKey);
|
this.account.getProfileStore().storeProfileKey(source, profileKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceAddress getSenderAddress(SignalServiceEnvelope envelope, SignalServiceContent content) {
|
private static SignalServiceAddress getSenderAddress(
|
||||||
|
final SignalServiceEnvelope envelope,
|
||||||
|
final SignalServiceContent content
|
||||||
|
) {
|
||||||
final var serviceId = envelope.getSourceServiceId();
|
final var serviceId = envelope.getSourceServiceId();
|
||||||
if (!envelope.isUnidentifiedSender() && serviceId != null) {
|
if (!envelope.isUnidentifiedSender() && serviceId != null) {
|
||||||
return new SignalServiceAddress(serviceId);
|
return new SignalServiceAddress(serviceId);
|
||||||
@ -1145,6 +1201,24 @@ public final class IncomingMessageHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SignalServiceAddress getSenderAddress(
|
||||||
|
final SignalServiceEnvelope envelope,
|
||||||
|
final SignalServiceContent content,
|
||||||
|
final Exception exception
|
||||||
|
) {
|
||||||
|
final var source = getSenderAddress(envelope, content);
|
||||||
|
if (source != null) {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
if (exception instanceof InvalidEnvelopeContentException e && e.getSender() != null) {
|
||||||
|
final var sender = ServiceId.parseOrNull(e.getSender());
|
||||||
|
if (sender != null) {
|
||||||
|
return new SignalServiceAddress(sender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private DeviceAddress getSender(SignalServiceEnvelope envelope, SignalServiceContent content) {
|
private DeviceAddress getSender(SignalServiceEnvelope envelope, SignalServiceContent content) {
|
||||||
final var serviceId = envelope.getSourceServiceId();
|
final var serviceId = envelope.getSourceServiceId();
|
||||||
if (!envelope.isUnidentifiedSender() && serviceId != null) {
|
if (!envelope.isUnidentifiedSender() && serviceId != null) {
|
||||||
|
|||||||
@ -5,9 +5,12 @@ import org.junit.jupiter.api.Test;
|
|||||||
import org.signal.core.models.ServiceId.ACI;
|
import org.signal.core.models.ServiceId.ACI;
|
||||||
import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata;
|
import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata;
|
||||||
import org.whispersystems.signalservice.api.messages.EnvelopeContentValidator;
|
import org.whispersystems.signalservice.api.messages.EnvelopeContentValidator;
|
||||||
|
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||||
import org.whispersystems.signalservice.internal.push.BodyRange;
|
import org.whispersystems.signalservice.internal.push.BodyRange;
|
||||||
import org.whispersystems.signalservice.internal.push.Content;
|
import org.whispersystems.signalservice.internal.push.Content;
|
||||||
import org.whispersystems.signalservice.internal.push.DataMessage;
|
import org.whispersystems.signalservice.internal.push.DataMessage;
|
||||||
|
import org.whispersystems.signalservice.internal.push.EditMessage;
|
||||||
|
import org.whispersystems.signalservice.internal.push.Envelope;
|
||||||
import org.whispersystems.signalservice.internal.push.SyncMessage;
|
import org.whispersystems.signalservice.internal.push.SyncMessage;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -60,4 +63,68 @@ class IncomingMessageHandlerTest {
|
|||||||
assertEquals(List.of(new InvalidEnvelopeContentException.InvalidBodyRange(0, -1, 2, "MENTION")),
|
assertEquals(List.of(new InvalidEnvelopeContentException.InvalidBodyRange(0, -1, 2, "MENTION")),
|
||||||
exception.getInvalidBodyRanges());
|
exception.getInvalidBodyRanges());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void invalidEnvelopeContentReportsOutOfBoundsBodyRangeFromEditMessage() {
|
||||||
|
final var sender = ACI.parseOrThrow("2a04f0cc-199f-4b93-99d8-13c6b10a70de");
|
||||||
|
final var bodyRange = new BodyRange.Builder().start(5).length(1).style(BodyRange.Style.ITALIC).build();
|
||||||
|
final var dataMessage = new DataMessage.Builder().body("hello").bodyRanges(List.of(bodyRange)).build();
|
||||||
|
final var editMessage = new EditMessage.Builder().targetSentTimestamp(1L).dataMessage(dataMessage).build();
|
||||||
|
final var content = new Content.Builder().editMessage(editMessage).build();
|
||||||
|
final var metadata = new EnvelopeMetadata(sender, null, 2, false, null, sender, 1);
|
||||||
|
final var validationResult = new EnvelopeContentValidator.Result.Invalid(
|
||||||
|
"[EditMessage] Body range with out-of-bounds start/length!",
|
||||||
|
new Throwable());
|
||||||
|
|
||||||
|
final var exception = IncomingMessageHandler.createInvalidEnvelopeContentException(validationResult,
|
||||||
|
metadata,
|
||||||
|
content);
|
||||||
|
|
||||||
|
assertEquals(InvalidEnvelopeContentException.DATA_MESSAGE_BODY_RANGE_OUT_OF_BOUNDS, exception.getCode());
|
||||||
|
assertEquals(5, exception.getBodyLength());
|
||||||
|
assertEquals(List.of(new InvalidEnvelopeContentException.InvalidBodyRange(0, 5, 1, "STYLE_ITALIC")),
|
||||||
|
exception.getInvalidBodyRanges());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void invalidEnvelopeContentReportsOutOfBoundsBodyRangeFromQuote() {
|
||||||
|
final var sender = ACI.parseOrThrow("2a04f0cc-199f-4b93-99d8-13c6b10a70de");
|
||||||
|
final var bodyRange = new BodyRange.Builder().start(2).length(2).style(BodyRange.Style.MONOSPACE).build();
|
||||||
|
final var quote = new DataMessage.Quote.Builder().text("hey").bodyRanges(List.of(bodyRange)).build();
|
||||||
|
final var dataMessage = new DataMessage.Builder().body("outer body").quote(quote).build();
|
||||||
|
final var content = new Content.Builder().dataMessage(dataMessage).build();
|
||||||
|
final var metadata = new EnvelopeMetadata(sender, null, 2, false, null, sender, 1);
|
||||||
|
final var validationResult = new EnvelopeContentValidator.Result.Invalid(
|
||||||
|
"[DataMessage] Quote body range with out-of-bounds start/length!",
|
||||||
|
new Throwable());
|
||||||
|
|
||||||
|
final var exception = IncomingMessageHandler.createInvalidEnvelopeContentException(validationResult,
|
||||||
|
metadata,
|
||||||
|
content);
|
||||||
|
|
||||||
|
assertEquals(InvalidEnvelopeContentException.DATA_MESSAGE_BODY_RANGE_OUT_OF_BOUNDS, exception.getCode());
|
||||||
|
assertEquals(3, exception.getBodyLength());
|
||||||
|
assertEquals(List.of(new InvalidEnvelopeContentException.InvalidBodyRange(0, 2, 2, "STYLE_MONOSPACE")),
|
||||||
|
exception.getInvalidBodyRanges());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void invalidSealedSenderCanBeResolvedForBlocking() {
|
||||||
|
final var sender = ACI.parseOrThrow("2a04f0cc-199f-4b93-99d8-13c6b10a70de");
|
||||||
|
final var envelope = new SignalServiceEnvelope(new Envelope.Builder()
|
||||||
|
.type(Envelope.Type.UNIDENTIFIED_SENDER)
|
||||||
|
.clientTimestamp(1L)
|
||||||
|
.build(), 2L);
|
||||||
|
final var exception = new InvalidEnvelopeContentException("invalid body range",
|
||||||
|
InvalidEnvelopeContentException.DATA_MESSAGE_BODY_RANGE_OUT_OF_BOUNDS,
|
||||||
|
sender.toString(),
|
||||||
|
2,
|
||||||
|
5,
|
||||||
|
List.of(),
|
||||||
|
new Throwable());
|
||||||
|
|
||||||
|
final var source = IncomingMessageHandler.getSenderAddress(envelope, null, exception);
|
||||||
|
|
||||||
|
assertEquals(sender, source.getServiceId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user