-
Notifications
You must be signed in to change notification settings - Fork 1.8k
validate resource name format before PopFirst in bundle parsing #16221
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -400,7 +400,8 @@ ResourcePath BundleSerializer::DecodeName(JsonReader& reader, | |
| } | ||
| auto path = | ||
| ResourcePath::FromString(document_name.get_ref<const std::string&>()); | ||
| if (!rpc_serializer_.IsLocalResourceName(path)) { | ||
| if (!rpc_serializer_.IsLocalResourceName(path) || path.size() < 5 || | ||
| path[4] != "documents") { | ||
| reader.Fail("Resource name is not valid for current instance: " + | ||
| path.CanonicalString()); | ||
| return {}; | ||
|
|
@@ -647,6 +648,11 @@ BundledDocumentMetadata BundleSerializer::DecodeDocumentMetadata( | |
| if (!reader.ok()) { | ||
| return {}; | ||
| } | ||
| if (!DocumentKey::IsDocumentKey(path)) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. - if (!DocumentKey::IsDocumentKey(path)) {
+ if (path.empty() || !DocumentKey::IsDocumentKey(path)) {
|
||
| reader.Fail("Resource name is not a valid document key: " + | ||
| path.CanonicalString()); | ||
| return {}; | ||
| } | ||
| DocumentKey key = DocumentKey(path); | ||
|
|
||
| SnapshotVersion read_time = DecodeSnapshotVersion( | ||
|
|
@@ -678,6 +684,11 @@ BundleDocument BundleSerializer::DecodeDocument(JsonReader& reader, | |
| if (!reader.ok()) { | ||
| return {}; | ||
| } | ||
| if (!DocumentKey::IsDocumentKey(path)) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. - if (!DocumentKey::IsDocumentKey(path)) {
+ if (path.empty() || !DocumentKey::IsDocumentKey(path)) { |
||
| reader.Fail("Resource name is not a valid document key: " + | ||
| path.CanonicalString()); | ||
| return {}; | ||
| } | ||
| DocumentKey key = DocumentKey(path); | ||
|
|
||
| SnapshotVersion update_time = DecodeSnapshotVersion( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1521,7 +1521,8 @@ bool Serializer::IsLocalResourceName(const ResourcePath& path) const { | |
|
|
||
| bool Serializer::IsLocalDocumentKey(absl::string_view path) const { | ||
| auto resource = ResourcePath::FromStringView(path); | ||
| return IsLocalResourceName(resource) && | ||
| return IsLocalResourceName(resource) && resource.size() >= 5 && | ||
| resource[4] == "documents" && | ||
| DocumentKey::IsDocumentKey(resource.PopFirst(5)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current implementation calls Suggested Diffbool Serializer::IsLocalDocumentKey(absl::string_view path) const {
auto resource = ResourcePath::FromStringView(path);
return IsLocalResourceName(resource) && resource.size() >= 5 &&
resource[4] == "documents" &&
- DocumentKey::IsDocumentKey(resource.PopFirst(5));
+ (resource.size() - 5) % 2 == 0;
} |
||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1166,6 +1166,39 @@ TEST_F(BundleSerializerTest, DecodeInvalidBundledDocumentMetadataFails) { | |
| } | ||
| } | ||
|
|
||
| TEST_F(BundleSerializerTest, DecodeNonDocumentKeyResourceNameFails) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current DecodeNonDocumentKeyResourceNameFails test only validates collection paths. Could you expand the test suite to verify that: Short paths (size < 5, e.g., /projects/p/databases/default) fail gracefully instead of crashing. for example: TEST_F(BundleSerializerTest, DecodeNonDocumentKeyResourceNameFails) {
// 1. Collection Path (Odd number of segments, fails key check)
{
ProtoDocument document = TestDocument(ProtoValue());
document.set_name(FullPath("bundle"));
std::string json_string;
MessageToJsonString(document, &json_string);
JsonReader reader;
bundle_serializer.DecodeDocument(reader, Parse(json_string));
EXPECT_NOT_OK(reader.status());
}
// 2. Root Path (Popped path is empty, size 0, must fail)
{
ProtoDocument document = TestDocument(ProtoValue());
document.set_name(FullPath(""));
std::string json_string;
MessageToJsonString(document, &json_string);
JsonReader reader;
bundle_serializer.DecodeDocument(reader, Parse(json_string));
EXPECT_NOT_OK(reader.status());
}
// 3. Short Path (Size < 5, must fail prefix validation instead of crashing PopFirst)
{
ProtoDocument document = TestDocument(ProtoValue());
document.set_name("projects/p/databases/default");
std::string json_string;
MessageToJsonString(document, &json_string);
JsonReader reader;
bundle_serializer.DecodeDocument(reader, Parse(json_string));
EXPECT_NOT_OK(reader.status());
}
// (Duplicate these three cases for DecodeDocumentMetadata below as well)
} |
||
| // A resource name that points at a collection (odd number of segments after | ||
| // the `documents` prefix) passes the local-resource-name check but is not a | ||
| // valid `DocumentKey`. It must be rejected gracefully instead of tripping the | ||
| // `HARD_ASSERT` inside the `DocumentKey` constructor. | ||
| { | ||
| ProtoDocument document = TestDocument(ProtoValue()); | ||
| document.set_name(FullPath("bundle")); | ||
| std::string json_string; | ||
| MessageToJsonString(document, &json_string); | ||
|
|
||
| JsonReader reader; | ||
| bundle_serializer.DecodeDocument(reader, Parse(json_string)); | ||
| EXPECT_NOT_OK(reader.status()); | ||
| } | ||
|
|
||
| { | ||
| ProtoBundledDocumentMetadata metadata; | ||
| metadata.set_name(FullPath("bundle")); | ||
| metadata.set_exists(true); | ||
| google::protobuf::Timestamp t1; | ||
| t1.set_seconds(0); | ||
| t1.set_nanos(0); | ||
| *metadata.mutable_read_time() = t1; | ||
| std::string json_string; | ||
| MessageToJsonString(metadata, &json_string); | ||
|
|
||
| JsonReader reader; | ||
| bundle_serializer.DecodeDocumentMetadata(reader, Parse(json_string)); | ||
| EXPECT_NOT_OK(reader.status()); | ||
| } | ||
| } | ||
|
|
||
| TEST_F(BundleSerializerTest, DecodeTargetWithoutImplicitOrderByOnName) { | ||
| std::string json( | ||
| R"({"name":"myNamedQuery", | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.