Add validation of snapshot FileInfo during parsing

Making sure that the file info that we read from the snapshot is still sane.
This commit is contained in:
Igor Motov 2015-07-07 10:31:09 -04:00
parent ae1ed34355
commit 8692292d0c
2 changed files with 81 additions and 3 deletions

View File

@ -26,6 +26,7 @@ import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.xcontent.ToXContent;
@ -298,7 +299,14 @@ public class BlobStoreIndexShardSnapshot {
}
}
}
// TODO: Verify???
// Verify that file information is complete
if (name == null || Strings.validFileName(name) == false) {
throw new ElasticsearchParseException("missing or invalid file name [" + name + "]");
} else if (physicalName == null || Strings.validFileName(physicalName) == false) {
throw new ElasticsearchParseException("missing or invalid physical file name [" + physicalName + "]");
} else if (length < 0) {
throw new ElasticsearchParseException("missing or invalid file length");
}
return new FileInfo(name, new StoreFileMetaData(physicalName, length, checksum, writtenBy, metaHash), partSize);
}

View File

@ -20,14 +20,17 @@ package org.elasticsearch.index.snapshots.blobstore;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.Version;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.xcontent.*;
import org.elasticsearch.index.store.StoreFileMetaData;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.index.snapshots.blobstore.BlobStoreIndexShardSnapshot.FileInfo.Fields;
import org.junit.Test;
import java.io.IOException;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
@ -44,8 +47,8 @@ public class FileInfoTest extends ElasticsearchTestCase {
for (int i = 0; i < hash.length; i++) {
hash.bytes[i] = randomByte();
}
StoreFileMetaData meta = new StoreFileMetaData("foobar", randomInt(), randomAsciiOfLengthBetween(1, 10), Version.LATEST, hash);
ByteSizeValue size = new ByteSizeValue(Math.max(0,Math.abs(randomLong())));
StoreFileMetaData meta = new StoreFileMetaData("foobar", Math.abs(randomLong()), randomAsciiOfLengthBetween(1, 10), Version.LATEST, hash);
ByteSizeValue size = new ByteSizeValue(Math.abs(randomLong()));
BlobStoreIndexShardSnapshot.FileInfo info = new BlobStoreIndexShardSnapshot.FileInfo("_foobar", meta, size);
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON).prettyPrint();
BlobStoreIndexShardSnapshot.FileInfo.toXContent(info, builder, ToXContent.EMPTY_PARAMS);
@ -67,4 +70,71 @@ public class FileInfoTest extends ElasticsearchTestCase {
assertThat(parsedInfo.isSame(info.metadata()), is(true));
}
}
@Test
public void testInvalidFieldsInFromXContent() throws IOException {
final int iters = scaledRandomIntBetween(1, 10);
for (int iter = 0; iter < iters; iter++) {
final BytesRef hash = new BytesRef(scaledRandomIntBetween(0, 1024 * 1024));
hash.length = hash.bytes.length;
for (int i = 0; i < hash.length; i++) {
hash.bytes[i] = randomByte();
}
String name = "foobar";
String physicalName = "_foobar";
String failure = null;
long length = Math.max(0,Math.abs(randomLong()));
// random corruption
switch (randomIntBetween(0, 3)) {
case 0:
name = "foo,bar";
failure = "missing or invalid file name";
break;
case 1:
physicalName = "_foo,bar";
failure = "missing or invalid physical file name";
break;
case 2:
length = -Math.abs(randomLong());
failure = "missing or invalid file length";
break;
case 3:
break;
default:
fail("shouldn't be here");
}
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
builder.startObject();
builder.field(Fields.NAME, name);
builder.field(Fields.PHYSICAL_NAME, physicalName);
builder.field(Fields.LENGTH, length);
builder.endObject();
byte[] xContent = builder.bytes().toBytes();
if (failure == null) {
// No failures should read as usual
final BlobStoreIndexShardSnapshot.FileInfo parsedInfo;
try (XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(xContent)) {
parser.nextToken();
parsedInfo = BlobStoreIndexShardSnapshot.FileInfo.fromXContent(parser);
}
assertThat(name, equalTo(parsedInfo.name()));
assertThat(physicalName, equalTo(parsedInfo.physicalName()));
assertThat(length, equalTo(parsedInfo.length()));
assertNull(parsedInfo.checksum());
assertNull(parsedInfo.metadata().checksum());
assertNull(parsedInfo.metadata().writtenBy());
} else {
try (XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(xContent)) {
parser.nextToken();
BlobStoreIndexShardSnapshot.FileInfo.fromXContent(parser);
fail("Should have failed with [" + failure + "]");
} catch (ElasticsearchParseException ex) {
assertThat(ex.getMessage(), containsString(failure));
}
}
}
}
}