Skip to content

Commit 42508ed

Browse files
feat: add SBS validation
1 parent 342c434 commit 42508ed

File tree

1 file changed

+114
-34
lines changed

1 file changed

+114
-34
lines changed

sdk/src/main/java/ly/count/android/sdk/ModuleConfiguration.java

Lines changed: 114 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,9 @@ public void run() {
128128
*/
129129
void loadConfigFromStorage(@Nullable String sdkBehaviorSettings) {
130130
String sConfig = storageProvider.getServerConfig();
131-
boolean providedSettingsUsed = false;
132131

133132
if (Utils.isNullOrEmpty(sConfig) && !Utils.isNullOrEmpty(sdkBehaviorSettings)) {
134133
sConfig = sdkBehaviorSettings;
135-
providedSettingsUsed = true;
136134
}
137135

138136
L.v("[ModuleConfiguration] loadConfigFromStorage, [" + sConfig + "]");
@@ -146,9 +144,7 @@ void loadConfigFromStorage(@Nullable String sdkBehaviorSettings) {
146144
latestRetrievedConfigurationFull = new JSONObject(sConfig);
147145
latestRetrievedConfiguration = latestRetrievedConfigurationFull.getJSONObject(keyRConfig);
148146
L.d("[ModuleConfiguration] loadStoredConfig, stored config loaded [" + sConfig + "]");
149-
if (providedSettingsUsed) {
150-
saveAndStoreDownloadedConfig(latestRetrievedConfigurationFull);
151-
}
147+
saveAndStoreDownloadedConfig(latestRetrievedConfigurationFull);
152148
} catch (JSONException e) {
153149
L.w("[ModuleConfiguration] loadStoredConfig, failed to parse, " + e);
154150

@@ -182,8 +178,8 @@ private <T> T extractValue(String key, StringBuilder sb, T currentValue, T defau
182178
return currentValue;
183179
}
184180

185-
private <T> T extractValue(String key, StringBuilder sb, T currentValue, T defaultValue, Class<T> clazz) {
186-
return extractValue(key, sb, currentValue, defaultValue, clazz, null);
181+
private Boolean extractValue(String key, StringBuilder sb, Boolean currentValue, Boolean defaultValue) {
182+
return extractValue(key, sb, currentValue, defaultValue, Boolean.class, null);
187183
}
188184

189185
//update the config variables according to the current config obj state
@@ -196,25 +192,25 @@ private void updateConfigVariables(@NonNull final CountlyConfig clyConfig) {
196192

197193
StringBuilder sb = new StringBuilder();
198194

199-
currentVNetworking = extractValue(keyRNetworking, sb, currentVNetworking, currentVNetworking, Boolean.class);
200-
currentVTracking = extractValue(keyRTracking, sb, currentVTracking, currentVTracking, Boolean.class);
201-
currentVSessionTracking = extractValue(keyRSessionTracking, sb, currentVSessionTracking, currentVSessionTracking, Boolean.class);
202-
currentVCrashReporting = extractValue(keyRCrashReporting, sb, currentVCrashReporting, currentVCrashReporting, Boolean.class);
203-
currentVViewTracking = extractValue(keyRViewTracking, sb, currentVViewTracking, currentVViewTracking, Boolean.class);
204-
currentVCustomEventTracking = extractValue(keyRCustomEventTracking, sb, currentVCustomEventTracking, currentVCustomEventTracking, Boolean.class);
205-
currentVLocationTracking = extractValue(keyRLocationTracking, sb, currentVLocationTracking, currentVLocationTracking, Boolean.class);
206-
currentVContentZone = extractValue(keyREnterContentZone, sb, currentVContentZone, currentVContentZone, Boolean.class);
195+
currentVNetworking = extractValue(keyRNetworking, sb, currentVNetworking, currentVNetworking);
196+
currentVTracking = extractValue(keyRTracking, sb, currentVTracking, currentVTracking);
197+
currentVSessionTracking = extractValue(keyRSessionTracking, sb, currentVSessionTracking, currentVSessionTracking);
198+
currentVCrashReporting = extractValue(keyRCrashReporting, sb, currentVCrashReporting, currentVCrashReporting);
199+
currentVViewTracking = extractValue(keyRViewTracking, sb, currentVViewTracking, currentVViewTracking);
200+
currentVCustomEventTracking = extractValue(keyRCustomEventTracking, sb, currentVCustomEventTracking, currentVCustomEventTracking);
201+
currentVLocationTracking = extractValue(keyRLocationTracking, sb, currentVLocationTracking, currentVLocationTracking);
202+
currentVContentZone = extractValue(keyREnterContentZone, sb, currentVContentZone, currentVContentZone);
207203
serverConfigUpdateInterval = extractValue(keyRServerConfigUpdateInterval, sb, serverConfigUpdateInterval, currentServerConfigUpdateInterval, Integer.class, (Integer value) -> value > 0);
208-
currentVRefreshContentZone = extractValue(keyRRefreshContentZone, sb, currentVRefreshContentZone, currentVRefreshContentZone, Boolean.class);
209-
currentVBackoffMechanism = extractValue(keyRBackoffMechanism, sb, clyConfig.backOffMechanismEnabled, currentVBackoffMechanism, Boolean.class);
204+
currentVRefreshContentZone = extractValue(keyRRefreshContentZone, sb, currentVRefreshContentZone, currentVRefreshContentZone);
205+
currentVBackoffMechanism = extractValue(keyRBackoffMechanism, sb, clyConfig.backOffMechanismEnabled, currentVBackoffMechanism);
210206
currentVBOMAcceptedTimeoutSeconds = extractValue(keyRBOMAcceptedTimeout, sb, currentVBOMAcceptedTimeoutSeconds, currentVBOMAcceptedTimeoutSeconds, Integer.class, (Integer value) -> value > 0);
211-
currentVBOMRQPercentage = extractValue(keyRBOMRQPercentage, sb, currentVBOMRQPercentage, currentVBOMRQPercentage, Double.class, (Double value) -> (value > 0.0 && value < 1.0));
207+
currentVBOMRQPercentage = extractValue(keyRBOMRQPercentage, sb, currentVBOMRQPercentage, currentVBOMRQPercentage, Double.class, (Double value) -> value > 0.0 && value < 1.0);
212208
currentVBOMRequestAge = extractValue(keyRBOMRequestAge, sb, currentVBOMRequestAge, currentVBOMRequestAge, Integer.class, (Integer value) -> value > 0);
213209
currentVBOMDuration = extractValue(keyRBOMDuration, sb, currentVBOMDuration, currentVBOMDuration, Integer.class, (Integer value) -> value > 0);
214210

215211
clyConfig.setMaxRequestQueueSize(extractValue(keyRReqQueueSize, sb, clyConfig.maxRequestQueueSize, clyConfig.maxRequestQueueSize, Integer.class, (Integer value) -> value > 0));
216212
clyConfig.setEventQueueSizeToSend(extractValue(keyREventQueueSize, sb, clyConfig.eventQueueSizeThreshold, Countly.sharedInstance().EVENT_QUEUE_SIZE_THRESHOLD, Integer.class, (Integer value) -> value > 0));
217-
clyConfig.setLoggingEnabled(extractValue(keyRLogging, sb, clyConfig.loggingEnabled, clyConfig.loggingEnabled, Boolean.class));
213+
clyConfig.setLoggingEnabled(extractValue(keyRLogging, sb, clyConfig.loggingEnabled, clyConfig.loggingEnabled));
218214
clyConfig.setUpdateSessionTimerDelay(extractValue(keyRSessionUpdateInterval, sb, clyConfig.sessionUpdateTimerDelay, Long.valueOf(Countly.TIMER_DELAY_IN_SECONDS).intValue(), Integer.class, (Integer value) -> value > 0));
219215
clyConfig.sdkInternalLimits.setMaxKeyLength(extractValue(keyRLimitKeyLength, sb, clyConfig.sdkInternalLimits.maxKeyLength, Countly.maxKeyLengthDefault, Integer.class, (Integer value) -> value > 0));
220216
clyConfig.sdkInternalLimits.setMaxValueSize(extractValue(keyRLimitValueSize, sb, clyConfig.sdkInternalLimits.maxValueSize, Countly.maxValueSizeDefault, Integer.class, (Integer value) -> value > 0));
@@ -223,7 +219,7 @@ private void updateConfigVariables(@NonNull final CountlyConfig clyConfig) {
223219
clyConfig.sdkInternalLimits.setMaxStackTraceLinesPerThread(extractValue(keyRLimitTraceLine, sb, clyConfig.sdkInternalLimits.maxStackTraceLinesPerThread, Countly.maxStackTraceLinesPerThreadDefault, Integer.class, (Integer value) -> value > 0));
224220
clyConfig.sdkInternalLimits.setMaxStackTraceLineLength(extractValue(keyRLimitTraceLength, sb, clyConfig.sdkInternalLimits.maxStackTraceLineLength, Countly.maxStackTraceLineLengthDefault, Integer.class, (Integer value) -> value > 0));
225221
clyConfig.content.setZoneTimerInterval(extractValue(keyRContentZoneInterval, sb, clyConfig.content.zoneTimerInterval, clyConfig.content.zoneTimerInterval, Integer.class, (Integer value) -> value >= 16));
226-
clyConfig.setRequiresConsent(extractValue(keyRConsentRequired, sb, clyConfig.shouldRequireConsent, clyConfig.shouldRequireConsent, Boolean.class));
222+
clyConfig.setRequiresConsent(extractValue(keyRConsentRequired, sb, clyConfig.shouldRequireConsent, clyConfig.shouldRequireConsent));
227223
clyConfig.setRequestDropAgeHours(extractValue(keyRDropOldRequestTime, sb, clyConfig.dropAgeHours, clyConfig.dropAgeHours, Integer.class, (Integer value) -> value >= 0));
228224

229225
String updatedValues = sb.toString();
@@ -233,29 +229,113 @@ private void updateConfigVariables(@NonNull final CountlyConfig clyConfig) {
233229
}
234230
}
235231

236-
void saveAndStoreDownloadedConfig(@NonNull JSONObject config) {
237-
L.v("[ModuleConfiguration] saveAndStoreDownloadedConfig");
232+
boolean validateServerConfig(@NonNull JSONObject config) {
233+
JSONObject newInner = config.optJSONObject(keyRConfig);
234+
235+
L.v("[ModuleConfiguration] validateServerConfig");
238236
if (!config.has(keyRVersion)) {
239-
L.w("[ModuleConfiguration] saveAndStoreDownloadedConfig, Retrieved configuration does not has a 'version' field. Config will be ignored.");
240-
return;
237+
L.w("[ModuleConfiguration] validateServerConfig, Retrieved configuration does not has a 'version' field. Config will be ignored.");
238+
return false;
239+
} else if (!config.has(keyRTimestamp)) {
240+
L.w("[ModuleConfiguration] validateServerConfig, Retrieved configuration does not has a 'timestamp' field. Config will be ignored.");
241+
return false;
242+
} else if (!config.has(keyRConfig)) {
243+
L.w("[ModuleConfiguration] validateServerConfig, Retrieved configuration does not has a 'configuration' field. Config will be ignored.");
244+
return false;
245+
} else if (config.length() != 3) {
246+
L.w("[ModuleConfiguration] validateServerConfig, Retrieved configuration does not have the expected number of keys. Config will be ignored.");
247+
return false;
248+
} else if (newInner == null || newInner.length() == 0) {
249+
L.d("[ModuleConfiguration] validateServerConfig, Config rejected: inner 'c' object is invalid or empty.");
250+
return false;
241251
}
242252

243-
if (!config.has(keyRTimestamp)) {
244-
L.w("[ModuleConfiguration] saveAndStoreDownloadedConfig, Retrieved configuration does not has a 'timestamp' field. Config will be ignored.");
245-
return;
246-
}
253+
removeUnsupportedKeys(newInner);
254+
return true;
255+
}
247256

248-
if (!config.has(keyRConfig)) {
249-
L.w("[ModuleConfiguration] saveAndStoreDownloadedConfig, Retrieved configuration does not has a 'configuration' field. Config will be ignored.");
250-
return;
257+
private void removeUnsupportedKeys(@NonNull JSONObject newInner) {
258+
Iterator<String> keys = newInner.keys();
259+
while (keys.hasNext()) {
260+
String key = keys.next();
261+
Object value = newInner.opt(key);
262+
263+
boolean isValid = false;
264+
265+
// --- Boolean keys ---
266+
switch (key) {
267+
case keyRNetworking:
268+
case keyRTracking:
269+
case keyRSessionTracking:
270+
case keyRCrashReporting:
271+
case keyRViewTracking:
272+
case keyRCustomEventTracking:
273+
case keyRLocationTracking:
274+
case keyREnterContentZone:
275+
case keyRRefreshContentZone:
276+
case keyRBackoffMechanism:
277+
case keyRLogging:
278+
case keyRConsentRequired:
279+
isValid = value instanceof Boolean;
280+
break;
281+
282+
// --- Positive Integer keys (> 0) ---
283+
case keyRServerConfigUpdateInterval:
284+
case keyRBOMAcceptedTimeout:
285+
case keyRBOMRequestAge:
286+
case keyRBOMDuration:
287+
case keyRReqQueueSize:
288+
case keyREventQueueSize:
289+
case keyRSessionUpdateInterval:
290+
case keyRLimitKeyLength:
291+
case keyRLimitValueSize:
292+
case keyRLimitSegValues:
293+
case keyRLimitBreadcrumb:
294+
case keyRLimitTraceLine:
295+
case keyRLimitTraceLength:
296+
isValid = value instanceof Integer && ((Integer) value) > 0;
297+
break;
298+
299+
// --- Integer >= 0 ---
300+
case keyRDropOldRequestTime:
301+
isValid = value instanceof Integer && ((Integer) value) >= 0;
302+
break;
303+
304+
// --- Integer >= 16 ---
305+
case keyRContentZoneInterval:
306+
isValid = value instanceof Integer && ((Integer) value) >= 16;
307+
break;
308+
309+
// --- Double between 0.0 and 1.0 ---
310+
case keyRBOMRQPercentage:
311+
isValid = value instanceof Double && ((Double) value > 0.0 && (Double) value < 1.0);
312+
break;
313+
// --- Unknown keys ---
314+
default:
315+
L.w("[ModuleConfiguration] removeUnsupportedKeys, Unknown key: [" + key + "], removing it. value: [" + value + "]");
316+
break;
317+
}
318+
319+
// --- If not valid or not known, remove it ---
320+
if (!isValid) {
321+
L.w("[ModuleConfiguration] removeUnsupportedKeys, Invalid or unknown key: [" + key + "], removing it. value: [" + value + "]");
322+
keys.remove();
323+
}
251324
}
325+
}
252326

253-
JSONObject newInner = config.optJSONObject(keyRConfig);
254-
if (newInner == null || newInner.length() == 0) {
255-
L.d("[ModuleConfiguration] Config rejected: inner 'c' object is invalid or empty.");
327+
void saveAndStoreDownloadedConfig(@NonNull JSONObject config) {
328+
L.v("[ModuleConfiguration] saveAndStoreDownloadedConfig");
329+
boolean validConfig = validateServerConfig(config);
330+
if (!validConfig) {
331+
L.w("[ModuleConfiguration] saveAndStoreDownloadedConfig, Retrieved configuration is not valid, ignoring it.");
332+
latestRetrievedConfigurationFull = null;
333+
latestRetrievedConfiguration = null;
256334
return;
257335
}
258336

337+
JSONObject newInner = config.optJSONObject(keyRConfig);
338+
assert newInner != null;
259339
if (latestRetrievedConfigurationFull == null) {
260340
latestRetrievedConfigurationFull = new JSONObject();
261341
latestRetrievedConfiguration = new JSONObject();

0 commit comments

Comments
 (0)