CVE-2022-39349 (published 2022-10-25T17:15Z)

The Tasks.org Android app is an open-source app for to-do lists and reminders. The Tasks.org app uses the activity `ShareLinkActivity.kt` to handle "share" intents coming from other components in the same device and convert them to tasks. Those intents may contain arbitrary file paths as attachments, in which case the files pointed by those paths are copied in the app\'s external storage directory. Prior to versions 12.7.1 and 13.0.1, those paths were not validated, allowing a malicious or compromised application in the same device to force Tasks.org to copy files from its internal storage to its external storage directory, where they became accessible to any component with permission to read the external storage. This vulnerability can lead to sensitive information disclosure. All information in the user\'s notes and the app\'s preferences, including the encrypted credentials of CalDav integrations if enabled, could be accessed by third party applications installed on the same device. This issue was fixed in versions 12.7.1 and 13.0.1. There are no known workarounds.

CVSS v3 base score: 5.5, MEDIUM (CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N)

Weakness information:
cwe_id: CWE-668
cwe_name: Exposure of Resource to Wrong Sphere
description: The product exposes a resource to the wrong control sphere, providing unintended actors with inappropriate access to the resource.
extended_description: 
            Resources such as files and directories may be inadvertently exposed through mechanisms such as insecure permissions, or when a program accidentally operates on the wrong object. For example, a program may intend that private files can only be provided to a specific user. This effectively defines a control sphere that is intended to prevent attackers from accessing these private files. If the file permissions are insecure, then parties other than the user will be able to access those files.
            A separate control sphere might effectively require that the user can only access the private files, but not any other files on the system. If the program does not ensure that the user is only requesting private files, then the user might be able to access other files on the system.
            In either case, the end result is that a resource has been exposed to the wrong party.
         
         

Fix information:
repo_url: https://github.com/tasks/tasks
commit_details: repo_url: https://github.com/tasks/tasks
author: Alex Baker
author_date: 2022-10-04 12:33:11-05:00
msg: Only accept content URIs in share activity
repository: repo_url: https://github.com/tasks/tasks
repo_name: tasks/tasks
description: Bringing Astrid Tasks back from the dead
repo_language: Kotlin
owner: tasks
file_changes: filename: CHANGELOG.md
change_type: ModificationType.MODIFY
diff: @@ -1,3 +1,7 @@
+### 12.7.1 (2022-10-04)
+
+* Bug fix
+
 ### 12.7 (2022-06-18)
 
 * Android 13 themed icon - Thanks @hanthor!

programming_language: Markdown
filename: build.gradle.kts
change_type: ModificationType.MODIFY
diff: @@ -56,8 +56,8 @@ android {
     defaultConfig {
         testApplicationId = "org.tasks.test"
         applicationId = "org.tasks"
-        versionCode = 120700
-        versionName = "12.7"
+        versionCode = 120701
+        versionName = "12.7.1"
         targetSdk = Versions.targetSdk
         minSdk = Versions.minSdk
         testInstrumentationRunner = "org.tasks.TestRunner"

programming_language: Scala
filename: ShareLinkActivity.kt
change_type: ModificationType.MODIFY
diff: @@ -1,15 +1,15 @@
 package com.todoroo.astrid.activity
 
+import android.content.ContentResolver
 import android.content.Intent
 import android.net.Uri
 import android.os.Bundle
 import androidx.lifecycle.lifecycleScope
-import com.google.common.io.Files
 import com.todoroo.astrid.data.Task
 import com.todoroo.astrid.service.TaskCreator
+import com.todoroo.astrid.utility.Constants
 import dagger.hilt.android.AndroidEntryPoint
 import kotlinx.coroutines.launch
-import org.tasks.Strings.isNullOrEmpty
 import org.tasks.analytics.Firebase
 import org.tasks.data.TaskAttachment
 import org.tasks.files.FileHelper
@@ -18,7 +18,6 @@ import org.tasks.intents.TaskIntents
 import org.tasks.preferences.Preferences
 import timber.log.Timber
 import javax.inject.Inject
-import kotlin.math.min
 
 /**
  * @author joshuagross
@@ -88,29 +87,24 @@ class ShareLinkActivity : InjectingAppCompatActivity() {
         startActivity(intent)
     }
 
-    private fun copyAttachment(intent: Intent): ArrayList<Uri> {
-        val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM) ?: return ArrayList()
-        var filename = FileHelper.getFilename(this, uri)
-        if (isNullOrEmpty(filename)) {
-            filename = intent.getStringExtra(Intent.EXTRA_SUBJECT)
-                    ?.takeIf { it.isNotBlank() }
-                    ?.let { it.substring(0, min(it.length, FileHelper.MAX_FILENAME_LENGTH)) }
-                    ?: uri.lastPathSegment
-        }
-        val basename = Files.getNameWithoutExtension(filename!!)
-        return arrayListOf(FileHelper.copyToUri(this, preferences.attachmentsDirectory!!, uri, basename))
-    }
+    private fun copyAttachment(intent: Intent): ArrayList<Uri> =
+        intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
+            ?.let { copyAttachments(listOf(it)) }
+            ?: arrayListOf()
+
+    private fun copyMultipleAttachments(intent: Intent): ArrayList<Uri> =
+        intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
+            ?.let { copyAttachments(it) }
+            ?: arrayListOf()
 
-    private fun copyMultipleAttachments(intent: Intent): ArrayList<Uri> {
-        val result = ArrayList<Uri>()
-        val uris = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
-        if (uris != null) {
-            for (uri in uris) {
-                result.add(FileHelper.copyToUri(this, preferences.attachmentsDirectory!!, uri))
+    private fun copyAttachments(uris: List<Uri>) =
+        uris
+            .filter {
+                it.scheme == ContentResolver.SCHEME_CONTENT
+                        && it.authority != Constants.FILE_PROVIDER_AUTHORITY
             }
-        }
-        return result
-    }
+            .map { FileHelper.copyToUri(this, preferences.attachmentsDirectory!!, it) }
+            .let { ArrayList(it) }
 
     private fun hasAttachments(intent: Intent) =
         intent.type?.let { type -> ATTACHMENT_TYPES.any { type.startsWith(it) } } ?: false

programming_language: Scala
filename: FileHelper.kt
change_type: ModificationType.MODIFY
diff: @@ -26,7 +26,6 @@ import java.io.IOException
 import java.util.*
 
 object FileHelper {
-    const val MAX_FILENAME_LENGTH = 40
     fun newFilePickerIntent(activity: Activity?, initial: Uri?, vararg mimeTypes: String?): Intent =
             Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
                 putExtra("android.content.extra.SHOW_ADVANCED", true)
@@ -178,20 +177,20 @@ object FileHelper {
 
     fun copyToUri(context: Context, destination: Uri, input: Uri): Uri {
         val filename = getFilename(context, input)
-        return copyToUri(context, destination, input, Files.getNameWithoutExtension(filename!!))
-    }
-
-    fun copyToUri(context: Context, destination: Uri, input: Uri, basename: String): Uri = try {
-        val output = newFile(
+        val basename = Files.getNameWithoutExtension(filename!!)
+        try {
+            val output = newFile(
                 context,
                 destination,
                 getMimeType(context, input),
                 basename,
-                getExtension(context, input))
-        copyStream(context, input, output)
-        output
-    } catch (e: IOException) {
-        throw IllegalStateException(e)
+                getExtension(context, input)
+            )
+            copyStream(context, input, output)
+            return output
+        } catch (e: IOException) {
+            throw IllegalStateException(e)
+        }
     }
 
     fun copyStream(context: Context, input: Uri?, output: Uri?) {

programming_language: Scala
filename: 120701.txt
change_type: ModificationType.ADD
diff: @@ -0,0 +1 @@
+* Bug fix

programming_language: Batchfile