Skip to content

feat(mobile): add lesson-only modules (Insecure Authorization, Supply Chain, Input Validation, Privacy Controls) - #1109

Open
SeanDuggan wants to merge 30 commits into
OWASP:devfrom
SeanDuggan:mobile-split/12-lesson-only-modules
Open

SeanDuggan wants to merge 30 commits into
OWASP:devfrom
SeanDuggan:mobile-split/12-lesson-only-modules

Conversation

@SeanDuggan

Copy link
Copy Markdown
Member

Summary

Adds four lesson-only Android modules, each with its server-side module row, lesson JSP, i18n strings and cheatsheet. This is one category per PR, following the app shell (#1068). Android app and module content only, with no platform changes.

Module Lesson Technique
Insecure Authorization 0f40ae03… Client-side role check in SharedPreferences
Supply Chain b9c1f4a7… Vulnerable bundled library leaks the key through its debug init log
Input Validation 708b7621… Deep-link host allow-list bypass (myapp://open?url=...)
Privacy Controls 952c4c37… Key hidden in the EXIF ImageDescription of a "shared" photo

What's included

  • Four lesson fragments, layouts and unit tests.
  • Four lesson JSPs and i18n properties, plus inputValidationStrings.properties.
  • coreSchema.sql: four module rows and four cheatSheetCreate calls.
  • solutions.properties: four cheatsheet entries.
  • Registration in KeyValidator, ModuleInfoHelper, MainActivity (nav map, drawer, titles), mobile_navigation.xml and strings.xml.
  • AndroidManifest.xml:
    • MainActivity is exported with singleTask and a myapp://open VIEW/BROWSABLE intent filter, needed by the Input Validation lesson.
    • A FileProvider (org.owasp.mobileshepherd.fileprovider) is added, needed by the Privacy Controls lesson.

Scoring

Keys are issued per user by the server (/mobileModuleStart and /mobileFlagGet) and checked by /mobileFlagSubmit. No key is hardcoded in the app. Tests assert that no hardcoded key field exists.

Testing

  • :app:testDebugUnitTest passes.
  • On an emulator against a local Tomcat 11 with the PR 2 servlets deployed (HTTPS only, signed-in user), each lesson was completed. The server recorded a core.results row for all four, and each moved to Completed in the drawer.
  • Input Validation: the deep link only validates after the async key is loaded. This is handled with keyReady / pendingDeepLink, and it was verified from a cold start.
  • Wording checked: no "challenge complete" text or counters appear in lesson content.

Depends on

SeanDuggan and others added 11 commits October 26, 2026 19:09
Adds the Gradle project, manifest, resources, launch/login/main activities,
navigation, preferences, authentication, key validation and progress
tracking utilities, and the home, progress, scoreboard and ADB reference
screens. Lesson and challenge screens referenced by the navigation graph are
added in the following PRs, so those destinations are not usable until then.

Part of the split of OWASP#894 (mobile app).
Disable cleartext traffic app-wide via a network security config and reject non-https server URLs at login, registration and key validation. Debug builds additionally trust user-installed CAs for local development.
The shell now builds with an empty KeyValidator.Module enum and empty
nav, menu and module-info registries. Each lesson/challenge category PR
registers its own entries. Removes the myapp://open deep link and the
KeyValidator unit test, which return with the first category.
Removes lesson/challenge strings and the Insufficient Cryptography ADB
reference card. These return with their own category PRs.
The app asks /mobileModuleStatus which modules the signed-in student can
open and greys out the rest in the navigation drawer and progress list,
with the server's reason shown when tapped. This mirrors the platform's
open floor / CTF rules: module closed, event not running, module blocked.

Advisory only: the server re-checks every module start and key request.
It fails open, so signed-out or offline use and failed requests grey out
nothing. Status refreshes on resume, drawer open, sign in and sign out.
…JDK path

Changing server_preference while signed in kept the old server's session
cookie, so it would be sent to the new server. Log out when the server
actually changes.

Remove org.gradle.java.home, which pinned a Windows Android Studio path and
overrode JAVA_HOME on other machines.
Replace the individually listed local planning docs with a single *.md rule.
Remove CHALLENGE_SOLUTIONS.txt and missing_challenges.txt, which refer to
no files in the repository.
… Chain, Input Validation, Privacy Controls)

Adds four lesson-only Android modules with their server-side module rows,
lesson JSPs, i18n strings and cheatsheets. Keys are issued per user by the
server (/mobileModuleStart + /mobileFlagGet) and validated via
/mobileFlagSubmit; nothing is hardcoded in the app.

- Insecure Authorization: client-side role check in SharedPreferences
- Supply Chain: vulnerable bundled library, key logged by its debug init
- Input Validation: deep-link allow-list bypass (myapp://open?url=...),
  with the MainActivity deep-link intent filter and onNewIntent handling
- Privacy Controls: key embedded in EXIF ImageDescription of a shared photo

Registers the modules in KeyValidator, ModuleInfoHelper, MainActivity and
navigation, and adds coreSchema.sql module rows plus cheatSheetCreate calls.
The logcat grep example rendered a literal "search_term" on the ADB Commands screen.
@SeanDuggan SeanDuggan added this to the 4.0.0 milestone Nov 2, 2026
@SeanDuggan
SeanDuggan requested a review from ismisepaul November 2, 2026 21:17
@SeanDuggan SeanDuggan self-assigned this Nov 2, 2026
SeanDuggan and others added 13 commits November 2, 2026 21:20
Add the Insecure Authorization, Input Validation, Privacy Controls and
Supply Chain topics (plus the M2 Mobile Top 10 entry) to
level-classification.toml and regenerate docs/mobile-levels.md so the
docs level-catalogue check passes.
…lter

The mobile category where clause drives open/close by category and the GetterIT module-count check. Categories added by this PR were missing, so those levels were counted as web levels.
Getter looks up each module's moduleNameLangPointer and 'category.' + moduleCategoryLangPointer in moduleNames.properties. The new levels had no entries, so the lookups threw MissingResourceException and the module, challenge and tournament listings came back incomplete.
Supply Chain, Privacy Controls, Input Validation and Insecure Authorization
now all announce completion with 'Lesson complete!'. Failure and
intermediate messages are unchanged.
Replace the simulated deep-link loader with an embedded WebView. The lesson
now has a genuine two-step flaw: a prefix-based allow-list
(url.startsWith("https://example.com")) that can be bypassed with
https://example.com@attacker.test/, and a JavaScript bridge registered on the
WebView that exposes the session token to every origin. The lesson completes
when a non-example.com page reads the token through the bridge.

Pages are served offline from app assets. Update the lesson text, the
cheatsheet solution and the unit tests to match.
…main

Replace example.com with toomanysheep.com in the lesson allow-list, trusted asset page, hint text, unit tests and cheat sheet.
The lesson now shows only 'Lesson complete!' on success, matching the other lessons.
The Android app lives in mobile/, not mobile/MobileShepherd/.
Insecure Communication is deferred to 4.0.1. Remove its key hash
entries, ProGuard rules and the cleartext-traffic comment.
…les' into mobile-split/12-lesson-only-modules
…ng to it

The lesson footer now names the risk (for example 'OWASP Mobile Top 10 - M4: Insufficient Input/Output Validation') without an external URL, and no uri.properties key is required.
Drop the About dialog button that opened owasp.org and the unused hidden Top 10 FAB with its string. Lessons now name the relevant risk as plain text.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

2 participants