The snippet below shows sample code that creates a file in external storage without using scoped storage APIs. The getExternalStorageDirectory API returns a path to the root of the shared external storage (e.g. /storage/emulated/0).
This requires special app access called "All files access", so the MANAGE_EXTERNAL_STORAGE permission must be declared in the manifest file.
packageorg.owasp.mastestappimportandroid.content.Contextimportandroid.os.Environmentimportandroid.util.Logimportjava.io.Fileimportjava.io.FileOutputStreamimportjava.io.IOExceptionclassMastgTest(privatevalcontext:Context){funmastgTest():String{valexternalStorageDir=Environment.getExternalStorageDirectory()valfileName=File(externalStorageDir,"secret.txt")valfileContent="Secret not using scoped storage"try{FileOutputStream(fileName).use{output->output.write(fileContent.toByteArray())Log.d("WriteExternalStorage","File written to external storage successfully.")}}catch(e:IOException){Log.e("WriteExternalStorage","Error writing file to external storage",e)return"ERROR!!\n\nError writing file to external storage. Do you have the MANAGE_EXTERNAL_STORAGE permission in the manifest and it's granted in 'All files access'?"}return"SUCCESS!!\n\nFile $fileName with content $fileContent saved to $externalStorageDir"}}
packageorg.owasp.mastestapp;importandroid.content.Context;importandroid.os.Environment;importandroid.util.Log;importjava.io.File;importjava.io.FileOutputStream;importjava.io.IOException;importkotlin.Metadata;importkotlin.io.CloseableKt;importkotlin.jvm.internal.Intrinsics;importkotlin.text.Charsets;/* compiled from: MastgTest.kt */@Metadata(d1={"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0000\b\u0007\u0018\u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u00020\u0003¢\u0006\u0002\u0010\u0004J\u0006\u0010\u0005\u001a\u00020\u0006R\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\u0007"},d2={"Lorg/owasp/mastestapp/MastgTest;","","context","Landroid/content/Context;","(Landroid/content/Context;)V","mastgTest","","app_debug"},k=1,mv={1,9,0},xi=48)/* loaded from: classes4.dex */publicfinalclassMastgTest{publicstaticfinalint$stable=8;privatefinalContextcontext;publicMastgTest(Contextcontext){Intrinsics.checkNotNullParameter(context,"context");this.context=context;}publicfinalStringmastgTest(){FileexternalStorageDir=Environment.getExternalStorageDirectory();FilefileName=newFile(externalStorageDir,"secret.txt");try{FileOutputStreamfileOutputStream=newFileOutputStream(fileName);try{FileOutputStreamoutput=fileOutputStream;byte[]bytes="Secret not using scoped storage".getBytes(Charsets.UTF_8);Intrinsics.checkNotNullExpressionValue(bytes,"this as java.lang.String).getBytes(charset)");output.write(bytes);Log.d("WriteExternalStorage","File written to external storage successfully.");CloseableKt.closeFinally(fileOutputStream,null);return"File "+fileName+" with content Secret not using scoped storage saved to "+externalStorageDir;}finally{}}catch(IOExceptione){Log.e("WriteExternalStorage","Error writing file to external storage",e);return"Error writing file to external storage. Do you have the MANAGE_EXTERNAL_STORAGE permission in the manifest and it's granted?";}}}
rules:-id:mastg-android-data-unencrypted-shared-storage-no-user-interaction-external-api-publicseverity:WARNINGlanguages:-javametadata:summary:Thisrulelooksformethodsthatreturnslocationsto"external storage"whichissharedwithotherappsmessage:"[MASVS-STORAGE] Make sure to encrypt files at these locations if necessary"pattern-either:-pattern:$X.getExternalStorageDirectory(...)-pattern:$X.getExternalStoragePublicDirectory(...)-pattern:$X.getDownloadCacheDirectory(...)-pattern:Intent.ACTION_CREATE_DOCUMENT-id:mastg-android-data-unencrypted-shared-storage-no-user-interaction-external-api-scopedseverity:WARNINGlanguages:-javametadata:summary:Thisrulelooksformethodsthatreturnslocationsto"scoped external storage"message:"[MASVS-STORAGE] These locations might be accessible to other apps on Android 10 and below given relevant permissions"pattern-either:-pattern:$X.getExternalFilesDir(...)-pattern:$X.getExternalFilesDirs(...)-pattern:$X.getExternalCacheDir(...)-pattern:$X.getExternalCacheDirs(...)-pattern:$X.getExternalMediaDirs(...)-id:mastg-android-data-unencrypted-shared-storage-no-user-interaction-mediastoreseverity:WARNINGlanguages:-javametadata:summary:ThisrulescansforusesofMediaStoreAPIthatwritesdatatotheexternalstorage.Thisdatacanbeaccessedbyotherapps.message:"[MASVS-STORAGE] Make sure to want this data to be shared with other apps"pattern-either:-pattern:importandroid.provider.MediaStore-pattern:$X.MediaStore
rules:-id:mastg-android-data-unencrypted-shared-storage-no-user-interaction-manifestseverity:WARNINGlanguages:-genericmetadata:summary:Thisrulescansforpermissionsthatallowsyourapptowritetoexternalstorageorsharedstoragemessage:"[MASVS-STORAGE] Make sure to encrypt files in external storage if necessary"pattern-either:-pattern:WRITE_EXTERNAL_STORAGE-pattern:MANAGE_EXTERNAL_STORAGE-pattern:ACCESS_ALL_EXTERNAL_STORAGE-pattern:requestLegacyExternalStorage="true"-pattern:preserveLegacyExternalStorage="true"-pattern:android:requestRawExternalStorageAccess="true"
The rule has identified one location in the code file where an API, getExternalStorageDirectory, is used to write to external storage as well as the location in the manifest file where the MANAGE_EXTERNAL_STORAGE permission is declared.
123456789
┌────────────────┐
│ 1 Code Finding │
└────────────────┘
MastgTest_reversed.java
rules.mastg-android-data-unencrypted-shared-storage-no-user-interaction-external-api-public
[MASVS-STORAGE] Make sure to encrypt files at these locations if necessary
27┆ File externalStorageDir = Environment.getExternalStorageDirectory();
123456789
┌────────────────┐
│ 1 Code Finding │
└────────────────┘
AndroidManifest_reversed.xml
rules.mastg-android-data-unencrypted-shared-storage-no-user-interaction-manifest
[MASVS-STORAGE] Make sure to encrypt files in external storage if necessary
2┆ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
After reviewing the decompiled code at the location specified in the output (file and line number) we can conclude that the test fails because the file written by this instance contains sensitive data, specifically a password.