Skip to content

Commit 536af84

Browse files
authored
Validate project-id returned from metadata server (#2822)
* Validate project-id returned from metadata server to handle cases in which a descriptive failure html page is returned instead of the projectid itself from server when projects are not running in google cloud machines. * update comments * Validate project-id returned from metadata server to handle cases in which a descriptive failure html page is returned instead of the projectid itself from server when projects are not running in google cloud machines. * update comments * fix code review problems * Fix code review problems * fix code review problems * fix useless parentheses
1 parent c3f741b commit 536af84

File tree

2 files changed

+35
-1
lines changed

2 files changed

+35
-1
lines changed

google-cloud-core/src/main/java/com/google/cloud/ServiceOptions.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,8 @@ private static String getAppEngineProjectIdFromMetadataServer() throws IOExcepti
452452
.setReadTimeout(500)
453453
.setHeaders(new HttpHeaders().set("Metadata-Flavor", "Google"));
454454
HttpResponse response = request.execute();
455-
return response.parseAsString();
455+
String projectId = response.parseAsString();
456+
return projectId != null && isValidProjectId(projectId)? projectId : null;
456457
}
457458

458459
protected static String getServiceAccountProjectId() {
@@ -476,6 +477,29 @@ static String getServiceAccountProjectId(String credentialsPath) {
476477
return project;
477478
}
478479

480+
/*
481+
* Returns true if the projectId is valid. This method checks whether the projectId
482+
* contains only lowercase letters, digits and hyphens, starts with a lowercase letter
483+
* and does not end with a hyphen, but does not check the length of projectId. This
484+
* method is primarily used to protect against DNS hijacking.
485+
*/
486+
static boolean isValidProjectId(String projectId) {
487+
for (char c : projectId.toCharArray()) {
488+
if (!isLowerCase(c) && !isDigit(c) && c != '-') {
489+
return false;
490+
}
491+
}
492+
return projectId.length() > 0 && isLowerCase(projectId.charAt(0))
493+
&& !projectId.endsWith("-");
494+
}
495+
496+
private static boolean isLowerCase(char c) {
497+
return c >= 'a' && c <= 'z';
498+
}
499+
500+
private static boolean isDigit(char c) {
501+
return c >= '0' && c <= '9';
502+
}
479503

480504
/**
481505
* Returns a Service object for the current service. For instance, when using Google Cloud

google-cloud-core/src/test/java/com/google/cloud/ServiceOptionsTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import static org.junit.Assert.assertSame;
2424
import static org.junit.Assert.assertTrue;
2525
import static org.junit.Assert.fail;
26+
import static com.google.common.truth.Truth.assertThat;
2627

2728
import com.google.api.core.ApiClock;
2829
import com.google.api.core.CurrentMillisClock;
@@ -313,4 +314,13 @@ public void testGetServiceAccountProjectId_nonExistentFile() throws Exception {
313314

314315
assertNull(ServiceOptions.getServiceAccountProjectId(credentialsFile.getPath()));
315316
}
317+
318+
@Test
319+
public void testValidateProjectId() throws Exception {
320+
assertThat(ServiceOptions.isValidProjectId("abc-123")).isTrue();
321+
assertThat(ServiceOptions.isValidProjectId("abc-123-ab")).isTrue();
322+
assertThat(ServiceOptions.isValidProjectId("abc=123")).isFalse();
323+
assertThat(ServiceOptions.isValidProjectId("abc123-")).isFalse();
324+
assertThat(ServiceOptions.isValidProjectId("1abc-23")).isFalse();
325+
}
316326
}

0 commit comments

Comments
 (0)