Skip to content

fix(verify): disclose skipped cross-checks and report URL schemes per platform - #3

Merged
mohn93 merged 3 commits into
mainfrom
fix/verify-disclosure-and-scheme-reporting
Sep 6, 2026
Merged

fix(verify): disclose skipped cross-checks and report URL schemes per platform#3
mohn93 merged 3 commits into
mainfrom
fix/verify-disclosure-and-scheme-reporting

Conversation

@mohn93

@mohn93 mohn93 commented Sep 6, 2026

Copy link
Copy Markdown
Member

Problem

Reported by a developer integrating flutter_ulink_sdk (domain abrezo.shared.ly) who saw ulink verify (v1.3.1) print a green PASS on a project whose deep-link setup had never actually been cross-checked.

Two distinct issues, both confirmed against the code:

  1. Silent green PASS. The AASA / assetlinks / dashboard cross-checks were not removed in v1.3.1 — they are gated on a successful authenticated project-config fetch. On an unauthenticated run (or a project selected without credentials) they are skipped, but the default report never counted or displayed skipped checks, so it printed a bare ✓ PASSED. A green run was therefore compatible with deep linking being completely unverified.

  2. URL schemes pooled across platforms. For a Flutter project the parser keeps correct per-platform lists but also builds a combined urlSchemes union. IosValidator and AndroidValidator printed that union under both platform headers, so verify -v showed an iOS-only scheme under Android and vice versa. (The actual scheme matching was already per-platform-correct — this was display only.)

Changes

  • VerificationReport: add skippedCount / hasSkipped.
  • verify: emit an explicit skip result whenever the dashboard cross-check does not run — now including the project-selected-but-no-credentials case, which previously produced no notice at all.
  • Default report: show the skipped count, a NOT VERIFIED section (what was not checked and why), and a qualified PARTIAL status. A clean ✓ PASSED is now reserved for a run with zero skips. Verbose report and JSON summary made consistent.
  • IosValidator / AndroidValidator: read iosUrlSchemes / androidUrlSchemes instead of the pooled urlSchemes.
  • Tests: guards against cross-platform scheme pooling and against a silent green PASS.

Behaviour (bare ulink verify, unauthenticated, real Flutter project)

flutter | ✓ 9 passed  ⊘ 1 skipped

⊘ NOT VERIFIED:
  Dashboard cross-check (bundle id, team id, package, fingerprints, AASA & assetlinks.json)
    No credentials — local files were NOT compared against the ULink dashboard
    config, and the hosted well-known files were not fetched.
    → Run "ulink login" ... Without this, verify only confirms local files exist —
      not that deep linking actually resolves.

⚠ PARTIAL - local checks passed, but 1 check was skipped. This is NOT a full verification.

Not addressed here (follow-ups)

  • pbxproj target selection. IosParser._extractVariableFromPbxproj still resolves PRODUCT_BUNDLE_IDENTIFIER globally ("first non-Test match in file order"), so in a multi-target project (app + extensions + tests) it can still resolve to an extension's id. Needs a target-scoped resolver; tracked separately.
  • Exit code. PARTIAL still exits 0 (incomplete, not failed) to avoid breaking curl … | ulink verify smoke tests and unauthenticated CI. A --strict flag to exit non-zero on skips can be a separate change.

Testing

dart analyze lib test clean. dart test — 296 pass (4 new guards).

… platform

verify silently degraded to local-only checks when it could not fetch the
dashboard config (unauthenticated, or a project selected without credentials)
and still printed a bare green PASS, so a passing run could coexist with deep
linking being unverified. It now emits an explicit skip notice, surfaces it in
the default report, and qualifies the result as PARTIAL unless every check ran.

Also fixes the iOS/Android URL scheme checks printing the pooled scheme union
(iOS + Android combined for Flutter) under each platform header; they now read
the per-platform lists, so an iOS-only scheme is no longer reported under
Android and vice versa.

- add skippedCount/hasSkipped to VerificationReport
- emit a skip result whenever the dashboard cross-check does not run, including
  the project-selected-but-no-credentials case that previously added none
- default report: show skipped count, a NOT VERIFIED section, and PARTIAL status
- ios/android validators read iosUrlSchemes/androidUrlSchemes, not urlSchemes
- tests: guards against cross-platform scheme pooling and a silent green PASS
The PR treated every skipped check as 'NOT VERIFIED', so a fully
authenticated, correctly configured project read as PARTIAL whenever an
optional probe could not run - no booted simulator, no adb, or a managed
Expo project with no native dirs. That is the common CI case, and crying
wolf on every run erodes the PARTIAL signal the disclosure fix introduced.

Add VerificationResult.blocksFullVerification and mark only the checks that
actually verify deep linking and were not performed - the dashboard
cross-check, and an aborted project selection. The report now:
- lists blocking skips under 'NOT VERIFIED' and optional ones under
  'SKIPPED (optional - did not affect the result)';
- downgrades to PARTIAL only when incompleteCount > 0 (report.isPartial),
  noting optional skips on an otherwise green PASS;
- adds summary.incomplete and a top-level partial flag to the JSON.

Optional runtime/managed-Expo skips keep the default (non-blocking).
Two accuracy fixes in the dashboard cross-check skip path:

- The skip message said 'Not authenticated' whenever effectiveProjectId
  was null, but that also happens for a signed-in user when no project
  exists, the selection was cancelled/invalid, or the project fetch failed.
  Gate the wording on hasCredentials and add a distinct 'signed in, but no
  project resolved' case with the right remediation.

- The dashboard upload set passed = !hasErrors, so a partial run (a check
  that would actually verify deep linking was skipped) uploaded passed:true
  while the console said NOT a full verification. Set passed = !hasErrors &&
  !isPartial so it matches the verdict; the separate partial flag still
  distinguishes the two.
@mohn93
mohn93 merged commit ff4eec5 into main Sep 6, 2026
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