-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Port MASTG-TEST-0036: Testing Enforced Updating (android) (by @appknox) #3462
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
sk3l10x1ng
wants to merge
24
commits into
OWASP:master
Choose a base branch
from
sk3l10x1ng:port_mastg_test_0036
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
2bb57d9
deprecated_notes
sk3l10x1ng 07109c2
added test-beta
sk3l10x1ng d11e628
added Demo
sk3l10x1ng a9708b1
updated test-beta
sk3l10x1ng d79bb96
Revert "added Demo"
sk3l10x1ng 46700ae
fix demo files
sk3l10x1ng b42de04
fix test-beta & demo.md
sk3l10x1ng a9d792a
fix lint
sk3l10x1ng 8e29eb0
fix spell & add rule
sk3l10x1ng 0673135
fixe DEMO-0062.md
sk3l10x1ng 486b13d
fix MainActivity.kt
sk3l10x1ng 428c988
Merge branch 'master' into port_mastg_test_0036
cpholguera ebf4d1c
MASTG-DEMO-0062.md
sk3l10x1ng 753f2ef
updated file id's
sk3l10x1ng 1173e1b
added build.gradle.kts.libs file
sk3l10x1ng 14961c2
updated MASTG-TEST-0x36
sk3l10x1ng 831a08d
updated changes
sk3l10x1ng d42e1d9
updated demo files
sk3l10x1ng fb39bc4
markdown_lint fix
sk3l10x1ng 29666ed
updated MainActivity.kt
sk3l10x1ng 28f7340
Merge branch 'master' into port_mastg_test_0036
cpholguera 30b04d2
removed comments
sk3l10x1ng 3e2e821
updated run.sh
sk3l10x1ng c801172
updated MASTG-TEST
sk3l10x1ng File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
31 changes: 31 additions & 0 deletions
31
demos/android/MASVS-CODE/MASTG-DEMO-0x36/MASTG-DEMO-0x36.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| --- | ||
| platform: android | ||
| title: Enforced Immediate Updates with Play Core API detected using semgrep | ||
| id: MASTG-DEMO-0x36 | ||
| code: [kotlin] | ||
| test: MASTG-TEST-0x336 | ||
| --- | ||
|
|
||
| ### Sample | ||
|
|
||
| The following code implements immediate in-app updates using the Google Play Core API by calling `startUpdateFlowForResult` with `AppUpdateOptions.newBuilder(1)`. | ||
sk3l10x1ng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
sk3l10x1ng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| {{ MastgTest.kt # MastgTest_reversed.java }} | ||
|
|
||
| ### Steps | ||
|
|
||
| Let's run @MASTG-TOOL-0110 rules against the sample code. | ||
|
|
||
| {{ ../../../../rules/mastg-android-enforced-updating.yml }} | ||
|
|
||
| {{ run.sh }} | ||
|
|
||
| ### Observation | ||
|
|
||
| The output file shows usages of the Google Play Core API enforcing immediate update. | ||
|
|
||
| {{ output.txt }} | ||
|
|
||
| ### Evaluation | ||
|
|
||
| The test passes because the app forces users to update the application immediately and does not provide a fallback. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| package org.owasp.mastestapp | ||
|
|
||
| import android.os.Bundle | ||
| import android.util.Log | ||
| import androidx.activity.ComponentActivity | ||
| import androidx.activity.compose.setContent | ||
| import androidx.activity.enableEdgeToEdge | ||
| import androidx.activity.result.ActivityResultLauncher | ||
| import androidx.activity.result.IntentSenderRequest | ||
| import androidx.activity.result.contract.ActivityResultContracts | ||
| import androidx.compose.foundation.layout.padding | ||
| import androidx.compose.material3.Text | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.ui.Modifier | ||
| import androidx.compose.ui.graphics.Color | ||
| import androidx.compose.ui.platform.testTag | ||
| import androidx.compose.ui.text.font.FontFamily | ||
| import androidx.compose.ui.tooling.preview.Preview | ||
| import androidx.compose.ui.unit.dp | ||
| import androidx.compose.ui.unit.sp | ||
|
|
||
| const val MASTG_TEXT_TAG = "mastgTestText" | ||
|
|
||
| class MainActivity : ComponentActivity() { | ||
sk3l10x1ng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| private val mastgTest by lazy { MastgTest(applicationContext) } | ||
| private lateinit var appUpdateResultLauncher: ActivityResultLauncher<IntentSenderRequest> | ||
|
|
||
| override fun onCreate(savedInstanceState: Bundle?) { | ||
| super.onCreate(savedInstanceState) | ||
| enableEdgeToEdge() | ||
|
|
||
| // mastgTest = MastgTest(applicationContext) | ||
sk3l10x1ng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| appUpdateResultLauncher = registerForActivityResult( | ||
| ActivityResultContracts.StartIntentSenderForResult() | ||
| ) { result -> | ||
| if (result.resultCode != RESULT_OK) { | ||
| Log.e( | ||
| "MainActivity", | ||
| "Update flow was cancelled or failed! Result code: ${result.resultCode}. Re-initiating." | ||
| ) | ||
| // The unused 'this' parameter is now removed. | ||
| mastgTest.checkForUpdate(appUpdateResultLauncher) | ||
sk3l10x1ng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } else { | ||
| Log.d("MainActivity", "Update accepted. The update is now in progress.") | ||
| } | ||
| } | ||
|
|
||
| setContent { | ||
| MainScreen( | ||
| displayString = "App is running. Checking for mandatory updates...", | ||
| onStartClick = { | ||
| // The unused 'this' parameter is now removed. | ||
| mastgTest.checkForUpdate(appUpdateResultLauncher) | ||
sk3l10x1ng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| ) | ||
| } | ||
|
|
||
| // The unused 'this' parameter is now removed. | ||
| mastgTest.checkForUpdate(appUpdateResultLauncher) | ||
sk3l10x1ng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| override fun onResume() { | ||
| super.onResume() | ||
| // if (::mastgTest.isInitialized) { | ||
| // The unused 'this' parameter is now removed. | ||
| mastgTest.resumeUpdateIfInProgress(appUpdateResultLauncher) | ||
sk3l10x1ng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // } | ||
| } | ||
| } | ||
|
|
||
| @Preview | ||
| @Composable | ||
| fun MainScreen( | ||
| displayString: String = "App is running.", | ||
sk3l10x1ng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| onStartClick: () -> Unit = {} | ||
| ) { | ||
| BaseScreen(onStartClick = onStartClick) { | ||
| Text( | ||
| modifier = Modifier | ||
| .padding(16.dp) | ||
| .testTag(MASTG_TEXT_TAG), | ||
| text = displayString, | ||
| color = Color.White, | ||
| fontSize = 16.sp, | ||
| fontFamily = FontFamily.Monospace | ||
| ) | ||
| } | ||
| } | ||
97 changes: 97 additions & 0 deletions
97
demos/android/MASVS-CODE/MASTG-DEMO-0x36/MainActivityKt_reversed.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| package org.owasp.mastestapp; | ||
|
|
||
| import androidx.compose.foundation.layout.PaddingKt; | ||
| import androidx.compose.material3.TextKt; | ||
| import androidx.compose.runtime.Composer; | ||
| import androidx.compose.runtime.ComposerKt; | ||
| import androidx.compose.runtime.RecomposeScopeImplKt; | ||
| import androidx.compose.runtime.ScopeUpdateScope; | ||
| import androidx.compose.runtime.internal.ComposableLambdaKt; | ||
| import androidx.compose.ui.Modifier; | ||
| import androidx.compose.ui.graphics.Color; | ||
| import androidx.compose.ui.platform.TestTagKt; | ||
| import androidx.compose.ui.text.TextLayoutResult; | ||
| import androidx.compose.ui.text.TextStyle; | ||
| import androidx.compose.ui.text.font.FontFamily; | ||
| import androidx.compose.ui.text.font.FontStyle; | ||
| import androidx.compose.ui.text.font.FontWeight; | ||
| import androidx.compose.ui.text.style.TextAlign; | ||
| import androidx.compose.ui.text.style.TextDecoration; | ||
| import androidx.compose.ui.unit.Dp; | ||
| import androidx.compose.ui.unit.TextUnitKt; | ||
| import kotlin.Metadata; | ||
| import kotlin.Unit; | ||
| import kotlin.jvm.functions.Function0; | ||
| import kotlin.jvm.functions.Function1; | ||
| import kotlin.jvm.functions.Function2; | ||
|
|
||
| /* compiled from: MainActivity.kt */ | ||
| @Metadata(d1 = {"\u0000\u0018\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\u0002\n\u0002\b\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\u001a'\u0010\u0002\u001a\u00020\u00032\b\b\u0002\u0010\u0004\u001a\u00020\u00012\u000e\b\u0002\u0010\u0005\u001a\b\u0012\u0004\u0012\u00020\u00030\u0006H\u0007¢\u0006\u0002\u0010\u0007\"\u000e\u0010\u0000\u001a\u00020\u0001X\u0086T¢\u0006\u0002\n\u0000¨\u0006\b"}, d2 = {"MASTG_TEXT_TAG", "", "MainScreen", "", "displayString", "onStartClick", "Lkotlin/Function0;", "(Ljava/lang/String;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;II)V", "app_debug"}, k = 2, mv = {2, 0, 0}, xi = 48) | ||
| /* loaded from: classes3.dex */ | ||
| public final class MainActivityKt { | ||
| public static final String MASTG_TEXT_TAG = "mastgTestText"; | ||
|
|
||
| /* JADX INFO: Access modifiers changed from: private */ | ||
| public static final Unit MainScreen$lambda$1(String str, Function0 function0, int i, int i2, Composer composer, int i3) { | ||
| MainScreen(str, function0, composer, RecomposeScopeImplKt.updateChangedFlags(i | 1), i2); | ||
| return Unit.INSTANCE; | ||
| } | ||
|
|
||
| public static final void MainScreen(final String displayString, final Function0<Unit> function0, Composer $composer, final int $changed, final int i) { | ||
| Composer $composer2 = $composer.startRestartGroup(-958245146); | ||
| ComposerKt.sourceInformation($composer2, "C(MainScreen)101@4458L280,101@4418L320:MainActivity.kt#vyvp3i"); | ||
| int $dirty = $changed; | ||
| int i2 = i & 1; | ||
| if (i2 != 0) { | ||
| $dirty |= 6; | ||
| } else if (($changed & 14) == 0) { | ||
| $dirty |= $composer2.changed(displayString) ? 4 : 2; | ||
| } | ||
| int i3 = i & 2; | ||
| if (i3 != 0) { | ||
| $dirty |= 48; | ||
| } else if (($changed & 112) == 0) { | ||
| $dirty |= $composer2.changedInstance(function0) ? 32 : 16; | ||
| } | ||
| if (($dirty & 91) != 18 || !$composer2.getSkipping()) { | ||
| if (i2 != 0) { | ||
| displayString = "App is running."; | ||
| } | ||
| if (i3 != 0) { | ||
| function0 = new Function0() { // from class: org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda0 | ||
| @Override // kotlin.jvm.functions.Function0 | ||
| public final Object invoke() { | ||
| return Unit.INSTANCE; | ||
| } | ||
| }; | ||
| } | ||
| BaseScreenKt.BaseScreen(function0, ComposableLambdaKt.rememberComposableLambda(891526107, true, new Function2<Composer, Integer, Unit>() { // from class: org.owasp.mastestapp.MainActivityKt.MainScreen.2 | ||
| @Override // kotlin.jvm.functions.Function2 | ||
| public /* bridge */ /* synthetic */ Unit invoke(Composer composer, Integer num) { | ||
| invoke(composer, num.intValue()); | ||
| return Unit.INSTANCE; | ||
| } | ||
|
|
||
| public final void invoke(Composer $composer3, int $changed2) { | ||
| ComposerKt.sourceInformation($composer3, "C102@4468L264:MainActivity.kt#vyvp3i"); | ||
| if (($changed2 & 11) != 2 || !$composer3.getSkipping()) { | ||
| TextKt.m2715Text4IGK_g(displayString, TestTagKt.testTag(PaddingKt.m681padding3ABfNKs(Modifier.INSTANCE, Dp.m6664constructorimpl(16)), MainActivityKt.MASTG_TEXT_TAG), Color.INSTANCE.m4219getWhite0d7_KjU(), TextUnitKt.getSp(16), (FontStyle) null, (FontWeight) null, (FontFamily) FontFamily.INSTANCE.getMonospace(), 0L, (TextDecoration) null, (TextAlign) null, 0L, 0, false, 0, 0, (Function1<? super TextLayoutResult, Unit>) null, (TextStyle) null, $composer3, 3504, 0, 130992); | ||
| return; | ||
| } | ||
| $composer3.skipToGroupEnd(); | ||
| } | ||
| }, $composer2, 54), $composer2, (($dirty >> 3) & 14) | 48, 0); | ||
| } else { | ||
| $composer2.skipToGroupEnd(); | ||
| } | ||
| ScopeUpdateScope scopeUpdateScopeEndRestartGroup = $composer2.endRestartGroup(); | ||
| if (scopeUpdateScopeEndRestartGroup != null) { | ||
| scopeUpdateScopeEndRestartGroup.updateScope(new Function2() { // from class: org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda1 | ||
| @Override // kotlin.jvm.functions.Function2 | ||
| public final Object invoke(Object obj, Object obj2) { | ||
| return MainActivityKt.MainScreen$lambda$1(displayString, function0, $changed, i, (Composer) obj, ((Integer) obj2).intValue()); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| package org.owasp.mastestapp | ||
|
|
||
| import android.content.Context | ||
| import android.util.Log | ||
| import androidx.activity.result.ActivityResultLauncher | ||
| import androidx.activity.result.IntentSenderRequest | ||
| import com.google.android.play.core.appupdate.AppUpdateManager | ||
| import com.google.android.play.core.appupdate.AppUpdateManagerFactory | ||
| import com.google.android.play.core.appupdate.AppUpdateOptions | ||
| import com.google.android.play.core.install.model.AppUpdateType | ||
| import com.google.android.play.core.install.model.UpdateAvailability | ||
|
|
||
| class MastgTest(context: Context) { | ||
|
|
||
| private val appUpdateManager: AppUpdateManager = AppUpdateManagerFactory.create(context) | ||
|
|
||
| /** | ||
| * Checks if an IMMEDIATE update is available on the Play Store. | ||
| */ | ||
| fun checkForUpdate( | ||
| appUpdateResultLauncher: ActivityResultLauncher<IntentSenderRequest> | ||
| ) { | ||
| Log.d("MastgTest", "Checking for an update...") | ||
| val appUpdateInfoTask = appUpdateManager.appUpdateInfo | ||
|
|
||
| appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> | ||
| val isUpdateAvailable = appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE | ||
| val isImmediateUpdateAllowed = appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) | ||
|
|
||
| if (isUpdateAvailable && isImmediateUpdateAllowed) { | ||
| Log.d("MastgTest", "Immediate update available. Starting flow.") | ||
| appUpdateManager.startUpdateFlowForResult( | ||
| appUpdateInfo, | ||
| appUpdateResultLauncher, | ||
| AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build() | ||
| ) | ||
| } else { | ||
| Log.d("MastgTest", "No immediate update available.") | ||
| } | ||
| }.addOnFailureListener { e -> | ||
| Log.e("MastgTest", "Failed to check for updates.", e) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Resumes an update that is already in progress. This is critical for onResume(). | ||
| */ | ||
| fun resumeUpdateIfInProgress( | ||
| appUpdateResultLauncher: ActivityResultLauncher<IntentSenderRequest> | ||
| ) { | ||
| appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo -> | ||
| if (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) { | ||
| Log.d("MastgTest", "Resuming in-progress update.") | ||
| appUpdateManager.startUpdateFlowForResult( | ||
| appUpdateInfo, | ||
| appUpdateResultLauncher, | ||
| AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build() | ||
| ) | ||
sk3l10x1ng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.