Question

I am trying to sanitize some user input on my app that will be sent server side. Based on the server settings, the data entered by the user may be used when creating or accessing files and folders. Obviously, sanitation will be also handled on the server but I will not and the app will not be able to control this due to the nature of the app. Therefore I feel it is my responsibility to have end to end sanitation where I or my app have control.

The preference I am trying to sanitize is for a photo album name that will be passed to the server.

According to the Log.d output this is working correctly. However, when I click the preference field to edit it, it shows the unsanitized input and when the preference is passed to the server it is also showing the unsanitized input.

FileUtil.sanitize(stringValue) is a simple method that trims leading and trailing spaces and removes ../ and ..\ from the string, for now atleast. I know there are others that I need to watch, just not 100% what they are yet.

So, the obvious question is, what am I missing? Is there an event that fires after this is complete that overwrites the changes that I am making here? I have a feeling that something is overwriting my changes because when I get the value immediately after the change, it shows the correct, sanitized data.

    /**
     * A preference value change listener that updates the preference's summary
     * to reflect its new value.
     */
    private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
        @Override
        public boolean onPreferenceChange(Preference preference, Object value) {
        String stringValue = value.toString();

        if (preference instanceof ListPreference) {
            // For list preferences, look up the correct display value in
            // the preference's 'entries' list.
            ListPreference listPreference = (ListPreference) preference;
            int index = listPreference.findIndexOfValue(stringValue);

            // Set the summary to reflect the new value.
            preference.setSummary(
                index >= 0
                    ? listPreference.getEntries()[index]
                    : null);

        } else if (preference.getKey().equals("text_default_album")) {
            Log.d(TAG, "default pref: "+preference.getKey()+" value: "+stringValue);
            stringValue = FileUtil.sanitize(stringValue);
            Log.d(TAG, "default pref: "+preference.getKey()+" value: "+stringValue);
            preference.setSummary(stringValue);

            SharedPreferences prefs = preference.getSharedPreferences();
            String def = prefs.getString("text_default_album", "");
            Boolean updated = prefs.edit().putString("text_default_album", stringValue).commit();
            String def2 = prefs.getString("text_default_album", "");
            Log.d(TAG, "def: "+def);
            Log.d(TAG, "def2: "+def2);
            Log.d(TAG, "updated: "+updated);
        } else {
            // For all other preferences, set the summary to the value's
            // simple string representation.
            preference.setSummary(stringValue);
        }
        return true;
        }
    };
Was it helpful?

Solution

I've resolved my issue by extending the EditTextPrefence class and overriding setText and getText

this is based on the answer I found here

package com.example.overrides;

import android.content.Context;
import android.preference.EditTextPreference;
import android.util.AttributeSet;

public class SanitizedEditTextPreference extends EditTextPreference {
    public SanitizedEditTextPreference(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public SanitizedEditTextPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SanitizedEditTextPreference(Context context) {
        super(context);
    }

    @Override
    public String getText() {
        String value = super.getText();
        return StringUtil.sanitize(value);
    }

    @Override
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
        super.setText(restoreValue ? getPersistedString(null) : (String) defaultValue);
    }

    @Override
    public void setText(String text) {
        if (StringUtil.isStringBlank(text)) {
            super.setText(null);
            return;
        }
        super.setText(StringUtil.sanitize(text));
    }
}

I simplified my previous code to:

/**
 * A preference value change listener that updates the preference's summary
 * to reflect its new value.
 */
private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
    @Override
    public boolean onPreferenceChange(Preference preference, Object value) {
    String stringValue = value.toString();

    if (preference instanceof ListPreference) {
        // For list preferences, look up the correct display value in
        // the preference's 'entries' list.
        ListPreference listPreference = (ListPreference) preference;
        int index = listPreference.findIndexOfValue(stringValue);

        // Set the summary to reflect the new value.
        preference.setSummary(
            index >= 0
                ? listPreference.getEntries()[index]
                : null);

    } else if (preference.getKey().equals("text_default_album")) {
        stringValue = StringUtil.sanitize(stringValue);
        preference.setSummary(stringValue);
    } else {
        // For all other preferences, set the summary to the value's
        // simple string representation.
        preference.setSummary(stringValue);
    }
    return true;
    }
};

and in the pref.xml file it is used like:

<com.example.overrides.SanitizedEditTextPreference
            android:key="text_default_album"
            android:title="@string/pref_title_default_album"
            android:defaultValue="@string/pref_default_album"
            android:selectAllOnFocus="true"
            android:inputType="text"
            android:capitalize="none"
            android:singleLine="true"
            android:maxLines="1" />
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top