Skip to content

Commit d1cac74

Browse files
Add limits, input validation, and unit tests for custom signals
1 parent 60ae034 commit d1cac74

File tree

3 files changed

+66
-2
lines changed

3 files changed

+66
-2
lines changed

firebase-config/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# limitations under the License.
1515
#
1616

17-
version=22.0.1
17+
version=22.0.2
1818
latestReleasedVersion=22.0.0
1919
android.enableUnitTestBinaryResources=true
2020

firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigSharedPrefsClient.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,15 @@ public void setCustomSignals(Map<String, Object> newCustomSignals) {
266266
String key = entry.getKey();
267267
Object value = entry.getValue();
268268

269+
// Validate value type, and key and value length
270+
if (value != null && !(value instanceof String || value instanceof Long)) {
271+
throw new IllegalArgumentException("Custom signal values must be of type String or Long");
272+
}
273+
if (key.length() > 250 || (value instanceof String && ((String) value).length() > 500)) {
274+
throw new IllegalArgumentException(
275+
"Custom signal keys must be 250 characters or less, and string values must be 500 characters or less.");
276+
}
277+
269278
// Merge new signals with existing ones, overwriting existing keys.
270279
// Also, remove entries where the new value is null.
271280
if (value != null) {
@@ -274,6 +283,16 @@ public void setCustomSignals(Map<String, Object> newCustomSignals) {
274283
existingCustomSignals.remove(key);
275284
}
276285
}
286+
287+
// Check if the map has actually changed and the size limit
288+
if (existingCustomSignals.equals(getCustomSignals())) {
289+
return;
290+
}
291+
if (existingCustomSignals.size() > 100) {
292+
throw new IllegalArgumentException(
293+
"Too many custom signals provided. The maximum allowed is 100.");
294+
}
295+
277296
frcMetadata
278297
.edit()
279298
.putString(CUSTOM_SIGNALS, new JSONObject(existingCustomSignals).toString())
@@ -289,7 +308,11 @@ public Map<String, Object> getCustomSignals() {
289308
Iterator<String> keys = existingCustomSignalsJson.keys();
290309
while (keys.hasNext()) {
291310
String key = keys.next();
292-
custom_signals.put(key, existingCustomSignalsJson.get(key));
311+
Object value = existingCustomSignalsJson.get(key);
312+
if (value instanceof Integer) {
313+
value = existingCustomSignalsJson.getLong(key);
314+
}
315+
custom_signals.put(key, value);
293316
}
294317
return custom_signals;
295318
} catch (JSONException e) {

firebase-config/src/test/java/com/google/firebase/remoteconfig/internal/ConfigSharedPrefsClientTest.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import static com.google.firebase.remoteconfig.internal.ConfigSharedPrefsClient.LAST_FETCH_TIME_NO_FETCH_YET;
2626
import static com.google.firebase.remoteconfig.internal.ConfigSharedPrefsClient.NO_BACKOFF_TIME;
2727
import static com.google.firebase.remoteconfig.internal.ConfigSharedPrefsClient.NO_FAILED_FETCHES;
28+
import static com.google.firebase.remoteconfig.testutil.Assert.assertThrows;
2829

2930
import android.content.Context;
3031
import android.content.SharedPreferences;
@@ -37,6 +38,7 @@
3738
import com.google.firebase.remoteconfig.internal.ConfigSharedPrefsClient.LastFetchStatus;
3839
import java.util.Collections;
3940
import java.util.Date;
41+
import java.util.HashMap;
4042
import java.util.Map;
4143
import org.junit.Before;
4244
import org.junit.Test;
@@ -367,4 +369,43 @@ public void getCustomSignals_isSet_returnsCustomSignals() {
367369
metadataClient.setCustomSignals(SAMPLE_CUSTOM_SIGNALS);
368370
assertThat(metadataClient.getCustomSignals()).isEqualTo(SAMPLE_CUSTOM_SIGNALS);
369371
}
372+
373+
@Test
374+
public void setCustomSignals_multipleTimes_addsNewSignals() {
375+
Map<String, Object> signals1 = ImmutableMap.of("subscription", "premium");
376+
Map<String, Object> signals2 = ImmutableMap.of("age", 20L);
377+
metadataClient.setCustomSignals(signals1);
378+
metadataClient.setCustomSignals(signals2);
379+
Map<String, Object> expectedSignals = ImmutableMap.of("subscription", "premium", "age", 20L);
380+
assertThat(metadataClient.getCustomSignals()).isEqualTo(expectedSignals);
381+
}
382+
383+
@Test
384+
public void setCustomSignals_nullValue_removesSignal() {
385+
Map<String, Object> signals1 = ImmutableMap.of("subscription", "premium", "age", 20L);
386+
metadataClient.setCustomSignals(signals1);
387+
Map<String, Object> signals2 = new HashMap<>();
388+
signals2.put("age", null);
389+
metadataClient.setCustomSignals(signals2);
390+
Map<String, Object> expectedSignals = ImmutableMap.of("subscription", "premium");
391+
assertThat(metadataClient.getCustomSignals()).isEqualTo(expectedSignals);
392+
}
393+
394+
@Test
395+
public void setCustomSignals_invalidInput_throwsException() {
396+
Map<String, Object> invalidSignals1 = new HashMap<>();
397+
invalidSignals1.put("name", true);
398+
assertThrows(
399+
IllegalArgumentException.class, () -> metadataClient.setCustomSignals(invalidSignals1));
400+
401+
Map<String, Object> invalidSignals2 = new HashMap<>();
402+
invalidSignals2.put("a".repeat(251), "value");
403+
assertThrows(
404+
IllegalArgumentException.class, () -> metadataClient.setCustomSignals(invalidSignals2));
405+
406+
Map<String, Object> invalidSignals3 = new HashMap<>();
407+
invalidSignals3.put("key", "a".repeat(501));
408+
assertThrows(
409+
IllegalArgumentException.class, () -> metadataClient.setCustomSignals(invalidSignals3));
410+
}
370411
}

0 commit comments

Comments
 (0)