fix: sanitize DNS preference inputs#32
Conversation
📝 WalkthroughWalkthroughDNS list inputs for ChangesDNS Input Sanitization
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Comment |
| private fun dnsReloadListener(preference: Preference, newValue: Any?): Boolean { | ||
| val rawValue = newValue as? String ?: return reloadListener.onPreferenceChange(preference, newValue) | ||
| val sanitizedValue = sanitizeDnsPreferenceValue(rawValue) | ||
| if (sanitizedValue != rawValue) { | ||
| if (preference is EditTextPreference) { | ||
| preference.text = sanitizedValue | ||
| } | ||
| needReload() | ||
| return false | ||
| } | ||
| needReload() | ||
| return true | ||
| } |
There was a problem hiding this comment.
needReload() is called unconditionally in both branches of the if/else, so it can be hoisted before the conditional to remove the duplication.
| private fun dnsReloadListener(preference: Preference, newValue: Any?): Boolean { | |
| val rawValue = newValue as? String ?: return reloadListener.onPreferenceChange(preference, newValue) | |
| val sanitizedValue = sanitizeDnsPreferenceValue(rawValue) | |
| if (sanitizedValue != rawValue) { | |
| if (preference is EditTextPreference) { | |
| preference.text = sanitizedValue | |
| } | |
| needReload() | |
| return false | |
| } | |
| needReload() | |
| return true | |
| } | |
| private fun dnsReloadListener(preference: Preference, newValue: Any?): Boolean { | |
| val rawValue = newValue as? String ?: return reloadListener.onPreferenceChange(preference, newValue) | |
| val sanitizedValue = sanitizeDnsPreferenceValue(rawValue) | |
| needReload() | |
| if (sanitizedValue != rawValue) { | |
| if (preference is EditTextPreference) { | |
| preference.text = sanitizedValue | |
| } | |
| return false | |
| } | |
| return true | |
| } |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Summary
Validation
Greptile Summary
This PR adds ISO control-character sanitization to the
remoteDnsanddirectDnspreference inputs at two layers: stripping control chars in the UI preference listener before persistence, and re-sanitizing each DNS entry during sing-box config generation as a defense-in-depth measure.SettingsPreferenceFragment.kt):dnsReloadListenersplits the raw input by line, strips ISO control characters per line withfilterNot { it.isISOControl() }, and persists the cleaned value viapreference.text = sanitizedValuebefore returningfalseto discard the raw input from the framework's default persistence path.ConfigBuilder.kt): A file-privatesanitizeDnsEntryfunction replaces the previous plaintrim()call on each split DNS entry; it addsfilterNot { it.isISOControl() }before trimming, ensuring any control characters stored before this fix are neutralized at config build time.Confidence Score: 5/5
Safe to merge; the sanitization logic is applied correctly at both layers and the value-persistence path through EditTextPreference works as intended.
Both sanitization paths are functionally sound. In the UI layer, preference.text = sanitizedValue calls persistString before return false discards the raw value, so the cleaned value reaches the datastore correctly. In ConfigBuilder the change is a straightforward augmentation of the existing trim() with an additional filterNot { it.isISOControl() }. No data-loss or incorrect-value scenarios were found in the changed code paths.
No files require special attention.
Important Files Changed
Sequence Diagram
%%{init: {'theme': 'neutral'}}%% sequenceDiagram participant U as User Input participant SP as SettingsPreferenceFragment participant DS as DataStore participant CB as ConfigBuilder U->>SP: Enter DNS value (may contain control chars) SP->>SP: sanitizeDnsPreferenceValue(rawValue) alt "sanitized != raw" SP->>DS: "preference.text = sanitizedValue (persist)" SP-->>U: return false (discard rawValue) else "sanitized == raw" SP->>DS: return true (framework persists rawValue) end SP->>SP: needReload() Note over CB,DS: Config generation (defense-in-depth) CB->>DS: DataStore.remoteDns / directDns CB->>CB: split(newline) CB->>CB: sanitizeDnsEntry(entry) per line CB->>CB: filterNot isISOControl + trim CB->>CB: takeIf isNotBlank and not comment%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant U as User Input participant SP as SettingsPreferenceFragment participant DS as DataStore participant CB as ConfigBuilder U->>SP: Enter DNS value (may contain control chars) SP->>SP: sanitizeDnsPreferenceValue(rawValue) alt "sanitized != raw" SP->>DS: "preference.text = sanitizedValue (persist)" SP-->>U: return false (discard rawValue) else "sanitized == raw" SP->>DS: return true (framework persists rawValue) end SP->>SP: needReload() Note over CB,DS: Config generation (defense-in-depth) CB->>DS: DataStore.remoteDns / directDns CB->>CB: split(newline) CB->>CB: sanitizeDnsEntry(entry) per line CB->>CB: filterNot isISOControl + trim CB->>CB: takeIf isNotBlank and not commentReviews (2): Last reviewed commit: "fix: type DNS sanitizing listeners" | Re-trigger Greptile