Skip to content

Add confirm=true import gated by trusted caller (adb/shell-only) - #3073

Draft
djbclark wants to merge 3 commits into
ImranR98:mainfrom
djbclark:pr/confirm-import-trusted
Draft

Add confirm=true import gated by trusted caller (adb/shell-only)#3073
djbclark wants to merge 3 commits into
ImranR98:mainfrom
djbclark:pr/confirm-import-trusted

Conversation

@djbclark

Copy link
Copy Markdown

This PR adds an optional confirm=true parameter to the
obtainium://apps/<json> deep-link that skips the import confirmation
dialog — but only when the intent came from a trusted caller (ADB/shell).

Usage

adb shell am start -d "obtainium://apps/<json>?confirm=true&headless=true"

When confirm=true is present and the caller is trusted, the catalog is
imported immediately. When combined with headless=true (from #3071),
the app exits after import.

Security model

confirm=true only takes effect when the intent was launched from a
privileged caller, detected via Activity.referrer == null in
MainActivity.kt. A null referrer means the ActivityManager launched
the activity directly (e.g. adb shell am start) — regular apps and
browsers always set a non-null referrer that identifies the calling app.

When the caller IS privileged, MainActivity.kt appends
confirmedBy=system to the URI before Flutter processes it. The Dart
side checks for this marker — a phishing link with ?confirm=true
alone won't bypass the dialog since it can't fabricate the
confirmedBy=system parameter.

Changes

  • MainActivity.kt: markTrustedIfPrivileged() checks referrer and
    appends confirmedBy=system for ADB/shell launches
  • home.dart: confirm=true is honored only when
    confirmedBy=system is present

Dependency

Depends on #3071 for the maybeExit() helper used in the headless
exit path.

djbclark added 3 commits July 14, 2026 00:16
obtainium://update/all?autoInstall=true&headless=true
obtainium://update?appId=id1,id2&silentOnly=true
obtainium://settings/installer?mode=shizuku&headless=true
obtainium://settings/background?enabled=true&wifiOnly=true
obtainium://settings/updates?interval=720&checkOnStart=true

The maybeExit() helper reads the headless/exit query param and calls
SystemNavigator.pop() after a short delay, enabling fully automated
workflows via Tasker, MacroDroid, ADB, or browser links.
Adds markTrustedIfPrivileged() which checks Activity.referrer.
A null referrer means the intent came from am start (ADB shell) or
the system — not from a browser or third-party app. In that case
confirmedBy=system is appended to the URI so the Dart side can
distinguish trusted launches from browser-based phishing links.
obtainium://apps/<json>?confirm=true&headless=true

confirm=true only takes effect when the intent came from a privileged
caller (ADB shell / am start), detected via Activity.referrer == null
in MainActivity.kt. Browser and third-party app links always set a
non-null referrer, so phishing links with ?confirm=true are ignored
and the confirmation dialog still appears.

The importHeadless param works independently — it exits after import
regardless of confirm=true, relying on PR 1's maybeExit() helper.
djbclark added a commit to djbclark/Obtainium that referenced this pull request Jul 14, 2026
Requires confirmedBy=system marker (set by MainActivity.kt when
Activity.referrer == null, i.e. ADB/shell launch) for confirm=true
to take effect. Browser phishing links can't fabricate this marker.

docs: update HANDOFF.md with PR ImranR98#3071 and ImranR98#3073 status
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant