Skip to content

feat(install-source): accept archive URLs from any public host - #3110

Merged
thymikee merged 2 commits into
callstack:mainfrom
alcpereira:feat/install-source-any-public-host
Oct 2, 2026
Merged

thymikee merged 2 commits into
callstack:mainfrom
alcpereira:feat/install-source-any-public-host

Conversation

@alcpereira

@alcpereira alcpereira commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

install-from-source URL sources from any public host can now be archives (or a bare .ipa on iOS). Previously iOS refused every URL outside GitHub Actions/EAS, and both platforms refused to extract archives from other hosts. Use case: a hub hands the device host a short-lived signed blob URL, so no GitHub token leaves the hub.

agent-device install-from-source "https://<signed-blob-host>/build.zip?sig=…" --platform ios

Threat model: the allowlist checked only the first URL and admitted any repo's artifact. Host-side safety comes from per-hop SSRF approval, cross-origin header stripping, byte caps, and extractArchiveSafely, which apply to every source. None of this makes app contents safe: a simulator .app is effectively host code, which any repo's artifact, public EAS URLs, and uploads already reached. Daemon auth is the boundary; security-trust.md now says so.

isTrustedInstallSourceUrl stays exported as @deprecated (public SDK since v0.11.16, used by a downstream bridge per #387), pending a maintainer decision on removal.

Lands after #3111 (host-kit directory-mode fix). 9 files.

Validation

Tested commit 1cf8109d8.

  • pnpm check:affected --run: all checks passed (vitest-related 364 files / 2381 tests).
  • The new iOS test fails on the old gate ("only supported for trusted artifact services").
  • Live run on an iOS 27.0 simulator: install-from-source https://github.com/callstack/agent-device/releases/download/v0.21.18/agent-device-ios-runner-0.21.18.app.tar.gz --platform ios (a releases/download URL, outside the allowlist) installed com.callstack.agentdevice.runner.uitests.xctrunner. The simulator's prior runner was restored afterwards.
  • Android archive URL: unit coverage only, no live run.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 9 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread website/docs/docs/client-api.md Outdated
@thymikee

thymikee commented Oct 2, 2026

Copy link
Copy Markdown
Member

The code looks fine at 61d15d0, but I can't call it ready yet because evidence is still pending: Smoke Tests is still running, and #3111 is not in this head. There are no conflicts.

I did not run the tests locally. I did not see the live iOS run output, only the PR body's description. Its URL is outside the old allowlist, so that run would have reached the new route. Android had no live run. The Android change only removes a host-side gate before materialization, and the device install of the extracted .apk is the same route that trusted-archive installs already used. Unit coverage exercises the real approval and extraction route. I did not review #3111 itself. I only confirmed it is not included in this head.

The smoke lanes install fixture apps through prepareAndroidInstallArtifact and prepareIosInstallArtifact, which this diff changes. Path sources behave the same as before, because allowArchiveExtraction was already true for every non-url source. A smoke failure would touch the changed functions but not the changed branch.

Not blocking, and you can take or leave these: the bullet at https://github.com/callstack/agent-device/blob/61d15d0/website/docs/docs/commands.md#L382 still says direct Android URL sources may be .apk or .aab, so it could also list iOS .ipa. Also, https://github.com/callstack/agent-device/blob/61d15d0/packages/provision-kit/src/install-source-archive.ts#L35 does not have #3111 yet. Without it, an archive that declares a 0o300 directory mode leaks its temp tree on cleanup (EACCES), and after this PR any public URL can serve such an archive.

Is there a smaller design than this change across the Android and Apple backends and provision-kit? I looked and found none. It already deletes the allowArchiveExtraction parameter, the assertArchiveExtractionAllowed guard and the iOS URL gate (net -27 production lines). The only leftover is the deprecated isTrustedInstallSourceUrl export, which now has no production caller. Removing it needs a maintainer decision to break the public ./install-source export, which has shipped since v0.11.16 and has a downstream user per #387.

Please merge #3111 first, or rebase onto it, and then wait for Smoke Tests to finish.

@thymikee

thymikee commented Oct 2, 2026

Copy link
Copy Markdown
Member

Update for 61d15d0: Smoke Tests finished with one failure, and it is likely unrelated. The failing test is RunnerTests.testSynthesizedReplacementPacesAnAppOwnedFieldAtItsAcknowledgeWindow, an iOS runner typing test (https://github.com/callstack/agent-device/actions/runs/36999435603/job/110813837773). This diff does not touch the runner or text input.

The code verdict is the same. The next blocker is still #3111: it now has a change request for the directory mode rule, and this PR needs it merged or included before it is ready.

alcpereira and others added 2 commits October 2, 2026 15:46
iOS refused every URL source outside GitHub Actions and EAS, and both
platforms refused to extract archives from other hosts. The allowlist
checked only the first URL and admitted any repository's artifact, so it
never established provenance. Host-side safety comes from per-hop SSRF
approval, cross-origin header stripping, byte caps, and
extractArchiveSafely, which apply to every source.

Remove the iOS gate and the allowArchiveExtraction plumbing.
isTrustedInstallSourceUrl stays exported as deprecated because it is
released SDK API.
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
@alcpereira
alcpereira force-pushed the feat/install-source-any-public-host branch from 61d15d0 to 262181f Compare October 2, 2026 14:46
@thymikee

thymikee commented Oct 2, 2026

Copy link
Copy Markdown
Member

Update for 262181f: this head is a clean rebase of 61d15d0 with the same patch, and it now includes #3111, which was the remaining blocker. The code verdict is the same, and there are no conflicts, so this is ready for human review.

Smoke Tests failed in the iOS simulator step that waits for the deep-link destination (https://github.com/callstack/agent-device/actions/runs/37022237282/job/110887924775). This is likely unrelated: the diff changes how archive URLs are accepted for install, and that step does not install from a URL.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Oct 2, 2026
@thymikee
thymikee merged commit 35b7b41 into callstack:main Oct 2, 2026
15 of 16 checks passed
thymikee added a commit to hassantsyed/agent-device that referenced this pull request Oct 3, 2026
…lugin

* origin/main: (628 commits)
  fix(ios): pin runner build roots under derived data (callstack#3158)
  feat: add managed provider plugin infrastructure (callstack#3121)
  fix(ios): report keyboard focus from the AX bridge's is-editing trait (callstack#3163)
  feat(devices): report model and osVersion (callstack#3119)
  fix: guard alert deadline before native tap synthesis (callstack#3113)
  feat(install-source): accept archive URLs from any public host (callstack#3110)
  test: keep uptime responsive behind busy runner work (callstack#3114)
  fix(apple): read the launch confirmation whenever the open cannot see the app (callstack#3115)
  fix(host-kit): keep extracted directories owner-accessible (callstack#3111)
  fix(daemon): run Apple tools with the requesting client's DEVELOPER_DIR (callstack#3109)
  fix(daemon): fence daemon.json removal to its owning process (callstack#3102)
  fix(snapshot): stop sibling-sized chrome containers from covering their own region (callstack#2996) (callstack#3097)
  fix(ios): stop reading windows past the one the runner resolved (callstack#3103)
  refactor(daemon): apply one dispatch-disclosure rule to returned and thrown failures (callstack#3099)
  chore: drop unused production exports and suppress dynamic consumers (callstack#3100)
  docs(help): document wait readiness and restart exhaustion (callstack#3098)
  docs: simplify Host to fresh Simlock devices and lease recovery (callstack#3095)
  feat(capture): report the display rotation a screenshot was rendered in (callstack#3088)
  refactor(snapshot): preserve normalized node attributes through presentation (callstack#3092)
  refactor(help): colocate fold guidance and extract workflows (callstack#3093)
  ...

# Conflicts:
#	README.md
#	package.json
#	packages/kernel/src/snapshot.ts
#	src/__tests__/eager-closure-budgets.ts
#	src/cli/commands/connection-presentation.ts
#	src/commands/schema/cli-help.ts
#	src/commands/schema/command-overrides.ts
amankansal-lt pushed a commit to LambdaTest/agent-device that referenced this pull request Oct 5, 2026
* origin/main:
  fix(ios): pin runner build roots under derived data (callstack#3158)
  feat: add managed provider plugin infrastructure (callstack#3121)
  fix(ios): report keyboard focus from the AX bridge's is-editing trait (callstack#3163)
  feat(devices): report model and osVersion (callstack#3119)
  fix: guard alert deadline before native tap synthesis (callstack#3113)
  feat(install-source): accept archive URLs from any public host (callstack#3110)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants