Allow user to ignore messages about vulnerable apps

This commit is contained in:
Peter Serwylo 2017-07-06 16:03:27 +10:00
parent 5f64985b34
commit 0551b0d1fc
8 changed files with 119 additions and 13 deletions

View File

@ -708,11 +708,20 @@ public class AppProvider extends FDroidProvider {
} }
private AppQuerySelection queryInstalledWithKnownVulns() { private AppQuerySelection queryInstalledWithKnownVulns() {
// Include the hash in this check because otherwise any app with any vulnerable version will String apk = getApkTableName();
// get returned.
String selection = " antiFeature." + Schema.AntiFeatureTable.Cols.NAME + " = 'KnownVuln' AND " + // Include the hash in this check because otherwise apps with any vulnerable version will
getApkTableName() + "." + ApkTable.Cols.HASH + " = installed." + InstalledAppTable.Cols.HASH; // get returned, rather than just the installed version.
return new AppQuerySelection(selection).requireNaturalInstalledTable().requireNatrualJoinAntiFeatures(); String compareHash = apk + "." + ApkTable.Cols.HASH + " = installed." + InstalledAppTable.Cols.HASH;
String knownVuln = " antiFeature." + Schema.AntiFeatureTable.Cols.NAME + " = 'KnownVuln' ";
String notIgnored = " COALESCE(prefs." + AppPrefsTable.Cols.IGNORE_VULNERABILITIES + ", 0) = 0 ";
String selection = knownVuln + " AND " + compareHash + " AND " + notIgnored;
return new AppQuerySelection(selection)
.requireNaturalInstalledTable()
.requireNatrualJoinAntiFeatures()
.requireLeftJoinPrefs();
} }
static AppQuerySelection queryPackageNames(String packageNames, String packageNameField) { static AppQuerySelection queryPackageNames(String packageNames, String packageNameField) {

View File

@ -93,6 +93,9 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
@Nullable @Nullable
private final Button actionButton; private final Button actionButton;
@Nullable
private final Button secondaryButton;
private final DisplayImageOptions displayImageOptions; private final DisplayImageOptions displayImageOptions;
@Nullable @Nullable
@ -137,11 +140,16 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
progressBar = (ProgressBar) itemView.findViewById(R.id.progress_bar); progressBar = (ProgressBar) itemView.findViewById(R.id.progress_bar);
cancelButton = (ImageButton) itemView.findViewById(R.id.cancel_button); cancelButton = (ImageButton) itemView.findViewById(R.id.cancel_button);
actionButton = (Button) itemView.findViewById(R.id.action_button); actionButton = (Button) itemView.findViewById(R.id.action_button);
secondaryButton = (Button) itemView.findViewById(R.id.secondary_button);
if (actionButton != null) { if (actionButton != null) {
actionButton.setOnClickListener(onActionClicked); actionButton.setOnClickListener(onActionClicked);
} }
if (secondaryButton != null) {
secondaryButton.setOnClickListener(onSecondaryButtonClicked);
}
if (cancelButton != null) { if (cancelButton != null) {
cancelButton.setOnClickListener(onCancelDownload); cancelButton.setOnClickListener(onCancelDownload);
} }
@ -213,6 +221,15 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
} }
} }
if (secondaryButton != null) {
if (viewState.shouldShowSecondaryButton()) {
secondaryButton.setVisibility(View.VISIBLE);
secondaryButton.setText(viewState.getSecondaryButtonText());
} else {
secondaryButton.setVisibility(View.GONE);
}
}
if (progressBar != null) { if (progressBar != null) {
if (viewState.showProgress()) { if (viewState.showProgress()) {
progressBar.setVisibility(View.VISIBLE); progressBar.setVisibility(View.VISIBLE);
@ -392,6 +409,18 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
} }
}; };
@SuppressWarnings("FieldCanBeLocal")
private final View.OnClickListener onSecondaryButtonClicked = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (currentApp == null) {
return;
}
onSecondaryButtonPressed(currentApp);
}
};
protected void onActionButtonPressed(@NonNull App app) { protected void onActionButtonPressed(@NonNull App app) {
// When the button says "Run", then launch the app. // When the button says "Run", then launch the app.
if (currentStatus != null && currentStatus.status == AppUpdateStatusManager.Status.Installed) { if (currentStatus != null && currentStatus.status == AppUpdateStatusManager.Status.Installed) {
@ -441,6 +470,9 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
} }
} }
/** To be overridden by subclasses if desired */
protected void onSecondaryButtonPressed(@NonNull App app) { }
@SuppressWarnings("FieldCanBeLocal") @SuppressWarnings("FieldCanBeLocal")
private final View.OnClickListener onCancelDownload = new View.OnClickListener() { private final View.OnClickListener onCancelDownload = new View.OnClickListener() {
@Override @Override

View File

@ -14,6 +14,7 @@ public class AppListItemState {
private final App app; private final App app;
private CharSequence mainText = null; private CharSequence mainText = null;
private CharSequence actionButtonText = null; private CharSequence actionButtonText = null;
private CharSequence secondaryButtonText = null;
private CharSequence statusText = null; private CharSequence statusText = null;
private CharSequence secondaryStatusText = null; private CharSequence secondaryStatusText = null;
private int progressCurrent = -1; private int progressCurrent = -1;
@ -34,6 +35,11 @@ public class AppListItemState {
return this; return this;
} }
public AppListItemState showSecondaryButton(CharSequence label) {
secondaryButtonText = label;
return this;
}
public AppListItemState setStatusText(CharSequence text) { public AppListItemState setStatusText(CharSequence text) {
this.statusText = text; this.statusText = text;
return this; return this;
@ -74,6 +80,14 @@ public class AppListItemState {
return actionButtonText; return actionButtonText;
} }
public boolean shouldShowSecondaryButton() {
return secondaryButtonText != null;
}
public CharSequence getSecondaryButtonText() {
return secondaryButtonText;
}
public boolean showProgress() { public boolean showProgress() {
return progressCurrent >= 0; return progressCurrent >= 0;
} }

View File

@ -14,6 +14,8 @@ import org.fdroid.fdroid.AppUpdateStatusManager;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.Apk;
import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.App;
import org.fdroid.fdroid.data.AppPrefs;
import org.fdroid.fdroid.data.AppPrefsProvider;
import org.fdroid.fdroid.data.AppProvider; import org.fdroid.fdroid.data.AppProvider;
import org.fdroid.fdroid.installer.Installer; import org.fdroid.fdroid.installer.Installer;
import org.fdroid.fdroid.installer.InstallerService; import org.fdroid.fdroid.installer.InstallerService;
@ -39,16 +41,17 @@ public class KnownVulnAppListItemController extends AppListItemController {
// TODO: Take into account signature when multi-sig stuff is merged. // TODO: Take into account signature when multi-sig stuff is merged.
if (app.installedVersionCode < app.suggestedVersionCode) { if (app.installedVersionCode < app.suggestedVersionCode) {
mainText = activity.getString(R.string.updates__app_with_known_vulnerability__upgrade, app.name); mainText = activity.getString(R.string.updates__app_with_known_vulnerability__prompt_upgrade, app.name);
actionButtonText = activity.getString(R.string.menu_upgrade); actionButtonText = activity.getString(R.string.menu_upgrade);
} else { } else {
mainText = activity.getString(R.string.updates__app_with_known_vulnerability__uninstall, app.name); mainText = activity.getString(R.string.updates__app_with_known_vulnerability__prompt_uninstall, app.name);
actionButtonText = activity.getString(R.string.menu_uninstall); actionButtonText = activity.getString(R.string.menu_uninstall);
} }
return new AppListItemState(app) return new AppListItemState(app)
.setMainText(mainText) .setMainText(mainText)
.showActionButton(actionButtonText); .showActionButton(actionButtonText)
.showSecondaryButton(activity.getString(R.string.updates__app_with_known_vulnerability__ignore));
} }
@Override @Override
@ -69,18 +72,32 @@ public class KnownVulnAppListItemController extends AppListItemController {
} }
} }
@Override
protected void onSecondaryButtonPressed(@NonNull App app) {
AppPrefs prefs = app.getPrefs(activity);
prefs.ignoreVulnerabilities = true;
AppPrefsProvider.Helper.update(activity, app, prefs);
refreshUpdatesList();
}
private void unregisterUninstallReceiver() { private void unregisterUninstallReceiver() {
LocalBroadcastManager.getInstance(activity).unregisterReceiver(installReceiver); LocalBroadcastManager.getInstance(activity).unregisterReceiver(installReceiver);
} }
/**
* Trigger the LoaderManager in UpdatesAdapter to automatically requery for the list of
* apps with known vulnerabilities (i.e. this app should no longer be in that list).
*/
private void refreshUpdatesList() {
activity.getContentResolver().notifyChange(AppProvider.getInstalledWithKnownVulnsUri(), null);
}
private final BroadcastReceiver installReceiver = new BroadcastReceiver() { private final BroadcastReceiver installReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) { switch (intent.getAction()) {
case Installer.ACTION_UNINSTALL_COMPLETE: case Installer.ACTION_UNINSTALL_COMPLETE:
// This will cause the LoaderManager in UpdatesAdapter to automatically requery for the list of refreshUpdatesList();
// apps with known vulnerabilities (i.e. this app should no longer be in that list).
activity.getContentResolver().notifyChange(AppProvider.getInstalledWithKnownVulnsUri(), null);
unregisterUninstallReceiver(); unregisterUninstallReceiver();
break; break;

View File

@ -63,4 +63,16 @@
app:layout_constraintTop_toBottomOf="@+id/app_name" app:layout_constraintTop_toBottomOf="@+id/app_name"
tools:text="Uninstall"/> tools:text="Uninstall"/>
<Button
android:id="@+id/secondary_button"
style="@style/DetailsSecondaryButtonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toStartOf="@+id/action_button"
app:layout_constraintTop_toBottomOf="@+id/app_name"
tools:text="Ignore"/>
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>

View File

@ -97,8 +97,9 @@ This often occurs with apps installed via Google Play or other sources, if they
<string name="updates__tts__download_app">Download</string> <string name="updates__tts__download_app">Download</string>
<string name="updates__tts__download_updates_for_all_apps">Download all updates</string> <string name="updates__tts__download_updates_for_all_apps">Download all updates</string>
<string name="updates__app_with_known_vulnerability__uninstall">We found a vulnerability with %1$s. We recommend uninstalling this app immediately.</string> <string name="updates__app_with_known_vulnerability__prompt_uninstall">We found a vulnerability with %1$s. We recommend uninstalling this app immediately.</string>
<string name="updates__app_with_known_vulnerability__upgrade">We found a vulnerability with %1$s. We recommend upgrading to the newest version immediately.</string> <string name="updates__app_with_known_vulnerability__prompt_upgrade">We found a vulnerability with %1$s. We recommend upgrading to the newest version immediately.</string>
<string name="updates__app_with_known_vulnerability__ignore">Ignore</string>
<string name="updates__hide_updateable_apps">Hide apps</string> <string name="updates__hide_updateable_apps">Hide apps</string>
<string name="updates__show_updateable_apps">Show apps</string> <string name="updates__show_updateable_apps">Show apps</string>

View File

@ -24,6 +24,10 @@
<item name="android:background">@drawable/button_secondary_background_selector</item> <item name="android:background">@drawable/button_secondary_background_selector</item>
</style> </style>
<style name="DetailsSecondaryButtonStyleSmall" parent="DetailsSecondaryButtonStyle">
<item name="android:padding">8dp</item>
</style>
<style name="DetailsMoreButtonStyle"> <style name="DetailsMoreButtonStyle">
<item name="android:padding">5dp</item> <item name="android:padding">5dp</item>
<item name="android:textSize">15sp</item> <item name="android:textSize">15sp</item>

View File

@ -6,6 +6,8 @@ import android.content.ContentValues;
import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.Apk;
import org.fdroid.fdroid.data.ApkProvider; import org.fdroid.fdroid.data.ApkProvider;
import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.App;
import org.fdroid.fdroid.data.AppPrefs;
import org.fdroid.fdroid.data.AppPrefsProvider;
import org.fdroid.fdroid.data.AppProvider; import org.fdroid.fdroid.data.AppProvider;
import org.fdroid.fdroid.data.FDroidProviderTest; import org.fdroid.fdroid.data.FDroidProviderTest;
import org.fdroid.fdroid.data.InstalledAppTestUtils; import org.fdroid.fdroid.data.InstalledAppTestUtils;
@ -107,6 +109,21 @@ public class AntiFeaturesTest extends FDroidProviderTest {
assertEquals(0, installed.size()); assertEquals(0, installed.size());
} }
@Test
public void allVulnerableButIgnored() {
install(allVuln, 101);
List<App> installed = AppProvider.Helper.findInstalledAppsWithKnownVulns(context);
assertEquals(1, installed.size());
App app = installed.get(0);
AppPrefs prefs = app.getPrefs(context);
prefs.ignoreVulnerabilities = true;
AppPrefsProvider.Helper.update(context, app, prefs);
List<App> installedButIgnored = AppProvider.Helper.findInstalledAppsWithKnownVulns(context);
assertEquals(0, installedButIgnored.size());
}
@Test @Test
public void antiFeaturesSaveCorrectly() { public void antiFeaturesSaveCorrectly() {
List<Apk> notVulnApks = ApkProvider.Helper.findByPackageName(context, notVuln.packageName); List<Apk> notVulnApks = ApkProvider.Helper.findByPackageName(context, notVuln.packageName);