Skip to content

Commit a0f26e0

Browse files
authored
Audit code to ensure printf style templates are compile time constants. (#15)
When the format string is not, it could be influenced by error messages or user input, and could possibly include illegal format specifiers (such as %p). While not as dangerous as in C/C++, this can cause exceptions to be thrown, which is especially bad, since the reason we're printf'ing is typically because we're in the middle of error handling. As part of this, I've audited usages of: - f.f.util.Assert.hardAssert - f.f.util.Assert.fail - f.f.util.Logger.warn - f.f.util.Logger.debug - java.lang.String.format
1 parent 9c63218 commit a0f26e0

File tree

15 files changed

+32
-33
lines changed

15 files changed

+32
-33
lines changed

firebase-firestore/src/androidTest/java/com/google/firebase/firestore/FirestoreTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,7 @@ public void testQuerySnapshotEventsForAdd() {
636636
assertFalse(doc2.getMetadata().hasPendingWrites());
637637
break;
638638
default:
639-
fail("unexpected call to onSnapshot:" + snapshot);
639+
fail("unexpected call to onSnapshot: " + snapshot);
640640
}
641641
});
642642
waitFor(emptyLatch);

firebase-firestore/src/androidTest/java/com/google/firebase/firestore/testutil/EventAccumulator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public EventAccumulator() {
3939
public EventListener<T> listener() {
4040
return (value, error) -> {
4141
synchronized (EventAccumulator.this) {
42-
hardAssert(error == null, "Unexpected error: " + error);
42+
hardAssert(error == null, "Unexpected error: %s", error);
4343
Log.i("EventAccumulator", "Received new event: " + value);
4444
events.add(value);
4545
checkFulfilled();

firebase-firestore/src/main/java/com/google/firebase/firestore/DocumentSnapshot.java

+8-9
Original file line numberDiff line numberDiff line change
@@ -505,15 +505,14 @@ private Object convertValue(FieldValue value, FieldValueOptions options) {
505505
// TODO: Somehow support foreign references.
506506
Logger.warn(
507507
"DocumentSnapshot",
508-
String.format(
509-
"Document %s contains a document reference within a different database "
510-
+ "(%s/%s) which is not supported. It will be treated as a reference in "
511-
+ "the current database (%s/%s) instead.",
512-
key.getPath(),
513-
refDatabase.getProjectId(),
514-
refDatabase.getDatabaseId(),
515-
database.getProjectId(),
516-
database.getDatabaseId()));
508+
"Document %s contains a document reference within a different database "
509+
+ "(%s/%s) which is not supported. It will be treated as a reference in "
510+
+ "the current database (%s/%s) instead.",
511+
key.getPath(),
512+
refDatabase.getProjectId(),
513+
refDatabase.getDatabaseId(),
514+
database.getProjectId(),
515+
database.getDatabaseId());
517516
}
518517
return new DocumentReference(key, firestore);
519518
} else {

firebase-firestore/src/main/java/com/google/firebase/firestore/core/RelationFilter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ private boolean matchesComparison(int comp) {
9494
case GREATER_THAN_OR_EQUAL:
9595
return comp >= 0;
9696
default:
97-
throw Assert.fail("Unknown operator: ", operator);
97+
throw Assert.fail("Unknown operator: %s", operator);
9898
}
9999
}
100100

firebase-firestore/src/main/java/com/google/firebase/firestore/core/SyncEngine.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ public void handleRejectedListen(int targetId, Status error) {
376376
handleRemoteEvent(event);
377377
} else {
378378
QueryView queryView = queryViewsByTarget.get(targetId);
379-
hardAssert(queryView != null, "Unknown target: " + targetId);
379+
hardAssert(queryView != null, "Unknown target: %s", targetId);
380380
localStore.releaseQuery(queryView.getQuery());
381381
removeAndCleanup(queryView);
382382
callback.onError(queryView.getQuery(), error);

firebase-firestore/src/main/java/com/google/firebase/firestore/local/LocalDocumentsView.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ private ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingCollection
143143
} else if (mutatedDoc instanceof Document) {
144144
results = results.insert(key, (Document) mutatedDoc);
145145
} else {
146-
throw fail("Unknown document type: " + mutatedDoc);
146+
throw fail("Unknown document type: %s", mutatedDoc);
147147
}
148148
}
149149
}

firebase-firestore/src/main/java/com/google/firebase/firestore/local/LocalSerializer.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ MaybeDocument decodeMaybeDocument(com.google.firebase.firestore.proto.MaybeDocum
6868
return decodeNoDocument(proto.getNoDocument());
6969

7070
default:
71-
throw fail("Unknown MaybeDocument " + proto);
71+
throw fail("Unknown MaybeDocument %s", proto);
7272
}
7373
}
7474

firebase-firestore/src/main/java/com/google/firebase/firestore/model/mutation/MutationBatchResult.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,9 @@ public static MutationBatchResult create(
5555
ByteString streamToken) {
5656
Assert.hardAssert(
5757
batch.getMutations().size() == mutationResults.size(),
58-
"Mutations sent "
59-
+ batch.getMutations().size()
60-
+ " must equal results received "
61-
+ mutationResults.size());
58+
"Mutations sent %d must equal results received %d",
59+
batch.getMutations().size(),
60+
mutationResults.size());
6261
ImmutableSortedMap<DocumentKey, SnapshotVersion> docVersions =
6362
DocumentCollections.emptyVersionMap();
6463
List<Mutation> mutations = batch.getMutations();

firebase-firestore/src/main/java/com/google/firebase/firestore/model/mutation/TransformMutation.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public MaybeDocument applyToLocalView(
126126
* method is guaranteed to be safe.
127127
*/
128128
private Document requireDocument(@Nullable MaybeDocument maybeDoc) {
129-
hardAssert(maybeDoc instanceof Document, "Unknown MaybeDocument type " + maybeDoc);
129+
hardAssert(maybeDoc instanceof Document, "Unknown MaybeDocument type %s", maybeDoc);
130130
Document doc = (Document) maybeDoc;
131131
hardAssert(doc.getKey().equals(getKey()), "Can only transform a document with the same key");
132132
return doc;

firebase-firestore/src/main/java/com/google/firebase/firestore/remote/OnlineStateTracker.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,10 @@ private void logClientOfflineWarningIfNecessary(String reason) {
187187
reason);
188188

189189
if (shouldWarnClientIsOffline) {
190-
Logger.warn(LOG_TAG, message);
190+
Logger.warn(LOG_TAG, "%s", message);
191191
shouldWarnClientIsOffline = false;
192192
} else {
193-
Logger.debug(LOG_TAG, message);
193+
Logger.debug(LOG_TAG, "%s", message);
194194
}
195195
}
196196

firebase-firestore/src/main/java/com/google/firebase/firestore/remote/RemoteSerializer.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ public com.google.firestore.v1beta1.Value encodeValue(FieldValue value) {
287287
DocumentKey key = (DocumentKey) encodedValue;
288288
builder.setReferenceValue(encodeResourceName(id, key.getPath()));
289289
} else {
290-
throw fail("Can't serialize " + value);
290+
throw fail("Can't serialize %s", value);
291291
}
292292

293293
return builder.build();
@@ -330,7 +330,7 @@ public FieldValue decodeValue(com.google.firestore.v1beta1.Value proto) {
330330
case MAP_VALUE:
331331
return decodeMapValue(proto.getMapValue());
332332
default:
333-
throw fail("Unknown value " + proto);
333+
throw fail("Unknown value %s", proto);
334334
}
335335
}
336336

@@ -446,7 +446,7 @@ public com.google.firestore.v1beta1.Write encodeMutation(Mutation mutation) {
446446
} else if (mutation instanceof DeleteMutation) {
447447
builder.setDelete(encodeKey(mutation.getKey()));
448448
} else {
449-
throw fail("unknown mutation type ", mutation.getClass());
449+
throw fail("unknown mutation type %s", mutation.getClass());
450450
}
451451

452452
if (!mutation.getPrecondition().isNone()) {
@@ -579,7 +579,8 @@ private FieldTransform decodeFieldTransform(DocumentTransform.FieldTransform fie
579579
hardAssert(
580580
fieldTransform.getSetToServerValue()
581581
== DocumentTransform.FieldTransform.ServerValue.REQUEST_TIME,
582-
"Unknown transform setToServerValue: " + fieldTransform.getSetToServerValue());
582+
"Unknown transform setToServerValue: %s",
583+
fieldTransform.getSetToServerValue());
583584
return new FieldTransform(
584585
FieldPath.fromServerFormat(fieldTransform.getFieldPath()),
585586
ServerTimestampOperation.getInstance());
@@ -857,7 +858,7 @@ private StructuredQuery.Filter encodeUnaryFilter(Filter filter) {
857858
} else if (filter instanceof NullFilter) {
858859
proto.setOp(UnaryFilter.Operator.IS_NULL);
859860
} else {
860-
throw fail("Unrecognized filter: " + filter.getCanonicalId());
861+
throw fail("Unrecognized filter: %s", filter.getCanonicalId());
861862
}
862863
return StructuredQuery.Filter.newBuilder().setUnaryFilter(proto).build();
863864
}

firebase-firestore/src/main/java/com/google/firebase/firestore/remote/TargetState.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ TargetChange toTargetChange() {
106106
removedDocuments = removedDocuments.insert(key);
107107
break;
108108
default:
109-
throw fail("Encountered invalid change type: " + changeType);
109+
throw fail("Encountered invalid change type: %s", changeType);
110110
}
111111
}
112112

firebase-firestore/src/main/java/com/google/firebase/firestore/remote/WatchChangeAggregator.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ public void handleTargetChange(WatchTargetChange targetChange) {
148148
}
149149
break;
150150
default:
151-
throw fail("Unknown target watch change state: " + targetChange.getChangeType());
151+
throw fail("Unknown target watch change state: %s", targetChange.getChangeType());
152152
}
153153
}
154154
}
@@ -187,7 +187,7 @@ public void handleExistenceFilter(ExistenceFilterWatchChange watchChange) {
187187
removeDocumentFromTarget(targetId, key, new NoDocument(key, SnapshotVersion.NONE));
188188
} else {
189189
hardAssert(
190-
expectedCount == 1, "Single document existence filter with count: " + expectedCount);
190+
expectedCount == 1, "Single document existence filter with count: %d", expectedCount);
191191
}
192192
} else {
193193
long currentSize = getCurrentDocumentCountForTarget(targetId);

firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,7 @@ T deserialize(
718718
if (throwOnUnknownProperties) {
719719
throw new RuntimeException(message);
720720
} else if (warnOnUnknownProperties) {
721-
Logger.warn(CustomClassMapper.class.getSimpleName(), message);
721+
Logger.warn(CustomClassMapper.class.getSimpleName(), "%s", message);
722722
}
723723
}
724724
}

firebase-firestore/src/test/java/com/google/firebase/firestore/spec/SpecTestCase.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ private Query parseQuery(Object querySpec) throws JSONException {
344344
}
345345
return query;
346346
} else {
347-
throw Assert.fail("Invalid query: " + querySpec);
347+
throw Assert.fail("Invalid query: %s", querySpec);
348348
}
349349
}
350350

@@ -761,7 +761,7 @@ private void doStep(JSONObject step) throws Exception {
761761
throw Assert.fail(
762762
"'applyClientState' is not supported on Android and should only be used in multi-client tests");
763763
} else {
764-
throw Assert.fail("Unknown step: " + step);
764+
throw Assert.fail("Unknown step: %s", step);
765765
}
766766
}
767767

0 commit comments

Comments
 (0)