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
1 change: 1 addition & 0 deletions .github/pr-test-selection.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"tests/Unit/V2/EmbeddedHttpSnapshotTest.php",
"tests/Unit/V2/ExecutionTracingTest.php",
"tests/Unit/V2/ExternalPayloadStorageTest.php",
"tests/Unit/V2/LaravelDependencySecurityPolicyTest.php",
"tests/Unit/V2/PlatformProtocolSpecsTest.php",
"tests/Unit/V2/ReadmeBranchDestinationsTest.php",
"tests/Unit/V2/ScopedCancellationContextTest.php",
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@

## Unreleased

- Workflow 2.21.0 requires PHP `^8.2` and patched Laravel
`^12.69.0|^13.30.0`. Laravel 10/11 and PHP 8.1 are no longer supported.
Upgrade the application platform first while preserving its existing
Workflow 2.20.x lock, then update Workflow. The stable-v1 authority and
ownership of existing durable history are unchanged.
- The embedded source/published upgrade matrices now cover Laravel 12/13
on their supported PHP 8.2–8.4 intersection. Laravel 13 still requires
PHP 8.3 or newer. Removed-framework constraints refuse before installation;
retired-platform advisory exceptions are not carried into 2.21.

## 2.20.4 - 2026-11-05

- Fix an embedded MySQL/InnoDB REPEATABLE READ deadlock between
continue-as-new and unrelated signal admission. Caller-owned transactions
retain their original read views, pending default/named signal handoffs
remain durable, and user workflow code is not automatically replayed to
recover from the deadlock.

## 2.20.3 - 2026-11-04

- Rollback, embedded Workflow package: 2.20.3 changes no schema, history
Expand Down
45 changes: 24 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,30 @@ This package also provides the orchestration engine hosted by
[Durable Workflow Cloud](https://cloud.durable-workflow.com/) when workers need
to run outside the Laravel application or across PHP, Python, and Rust.

Use a patched Laravel 12 or 13 release for production. Laravel 10 and 11
compatibility is retained for upgrading and draining existing applications, not
as security support for those frameworks. The [dependency security policy](resources/laravel-dependency-security-policy.json)
records the remaining advisories and review dates. The Laravel 10 and 11
minimums include their upstream TokenGuard fixes; the advisory feed still
includes those patched branches. Keep Composer audits and application security
updates enabled.

Workflow 2.17.0 no longer supports Laravel 9, which has no fix for the
[TokenGuard advisory](https://github.com/laravel/framework/security/advisories/GHSA-f9x3-7vq9-cph6).
Composer keeps a Laravel 9 application on Workflow 2.16.x, so updating does not
break it. To move past 2.16.x, upgrade the application to a patched Laravel 12
or 13 release first. Until then, do not expose token-guard authentication; see
[#922](https://github.com/durable-workflow/workflow/issues/922).

The legacy Laravel 10 and 11 exception requires an existing application
lockfile. Keep that lock, use a targeted Workflow update, and drain or migrate
to patched Laravel 12 or 13. It does not cover a fresh PHP 8.1 dependency
resolution, which Composer may refuse because of the legacy advisories. If
Composer refuses the migration, upgrade the framework first; do not globally
disable security blocking.
Workflow 2.21 requires **PHP 8.2 or newer** and **Laravel 12.69.0 or newer
on the 12.x line, or Laravel 13.30.0 or newer on the 13.x line**. Laravel 13
requires PHP 8.3. The [dependency security policy](resources/laravel-dependency-security-policy.json)
records the supported framework floors. Keep Composer audits and application
security updates enabled.

### Upgrade to 2.21

Upgrade PHP 8.1 applications to PHP 8.2 or newer and Laravel 10/11 applications
to patched Laravel 12 or 13 before requiring Workflow 2.21. Composer refuses
2.21 on the retired platforms. Keep the application's existing 2.20.x lock
while preparing that upgrade; older Workflow releases do not extend upstream
security support for PHP or Laravel. Do not disable advisory blocking to force
an upgrade.

This platform change does not migrate durable history. Existing v1 runs remain
owned by v1; retain their code and workers until they finish, and use the
[embedded upgrade contract](resources/laravel-embedded-upgrade-contract.json)
to choose drain or coexistence. The stable 1.x authority is unchanged.

Laravel 9 applications remain on their compatible Workflow 2.16.x line until
they upgrade the framework. Laravel 9 has no fix for the
[TokenGuard advisory](https://github.com/laravel/framework/security/advisories/GHSA-f9x3-7vq9-cph6)
and was removed in Workflow 2.17.0; see [#922](https://github.com/durable-workflow/workflow/issues/922).

## Install

Expand Down
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@
}
],
"require": {
"php": "^8.1",
"laravel/framework": "^10.50.4|^11.56.1|^12.69.0|^13.30.0",
"php": "^8.2",
"laravel/framework": "^12.69.0|^13.30.0",
"react/promise": "^2.9|^3.0",
"apache/avro": "^1.12",
"symfony/yaml": "^6.0|^7.0|^8.0"
Expand All @@ -71,7 +71,7 @@
"open-telemetry/api": "^1.10.1",
"open-telemetry/exporter-otlp": "^1.4",
"open-telemetry/sdk": "^1.0",
"orchestra/testbench": "^8.0|^11.0",
"orchestra/testbench": "^10.0|^11.0",
"phpstan/phpstan": "^2.0",
"symplify/easy-coding-standard": "^11.0"
},
Expand All @@ -87,7 +87,7 @@
"dev-main": "2.0.x-dev"
},
"durable-workflow": {
"product-train": "2.20.4",
"product-train": "2.21.0",
"laravel-embedded-upgrade-contract": "resources/laravel-embedded-upgrade-contract.json",
"laravel-dependency-security-policy": "resources/laravel-dependency-security-policy.json"
},
Expand Down
62 changes: 8 additions & 54 deletions resources/laravel-dependency-security-policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,6 @@
"reason": "The host application owns the installed Laravel version and lockfile; GitHub dependency alerts remain visible while the per-major Composer audit owns resolution and accepted-risk tracking for this library constraint."
},
"supported_majors": {
"10": {
"minimum": "10.50.4",
"disposition": "accepted-risk"
},
"11": {
"minimum": "11.56.1",
"disposition": "accepted-risk"
},
"12": {
"minimum": "12.69.0",
"disposition": "secure"
Expand All @@ -31,53 +23,15 @@
"9": {
"last_workflow_minor": "2.16",
"reason": "Laravel 9 has no fix for GHSA-f9x3-7vq9-cph6 (TokenGuard). Workflow 2.17.0 removes it from the supported constraint; Composer keeps Laravel 9 applications on Workflow 2.16.x until they upgrade the framework."
}
},
"accepted_advisories": {
"PKSA-d5tc-s1qs-h781": {
"github_advisory": "GHSA-jh5r-qr3c-85q8",
"affected_supported_majors": [
"10",
"11"
],
"review_by": "2026-11-24",
"reason": "Laravel has not published a patch on these legacy majors. Compatibility is retained for embedded upgrade and drain paths; production applications must disable APP_DEBUG and migrate to a patched Laravel 12 or 13 release."
},
"PKSA-m5cs-t1y6-qpcs": {
"github_advisory": "GHSA-crmm-hgp2-wgrp",
"affected_supported_majors": [
"10",
"11"
],
"review_by": "2026-11-24",
"reason": "Laravel has not published a patched release on these majors; compatibility is retained only for embedded upgrade and drain paths."
},
"PKSA-3r5d-mb8f-1qw9": {
"github_advisory": "GHSA-5vg9-5847-vvmq",
"affected_supported_majors": [
"10",
"11"
],
"review_by": "2026-11-24",
"reason": "Laravel has not published a patched release on these majors; compatibility is retained only for embedded upgrade and drain paths."
},
"PKSA-mdq4-51ck-6kdq": {
"github_advisory": "GHSA-5vg9-5847-vvmq",
"affected_supported_majors": [
"10",
"11"
],
"review_by": "2026-11-24",
"reason": "The FriendsOfPHP record describes the same GitHub advisory, and Laravel has not published a patched release on these majors."
"10": {
"last_workflow_minor": "2.20",
"reason": "Laravel 10 is outside upstream security support. Workflow 2.21.0 removes this major; retain the existing 2.20.x application lock while upgrading PHP to at least 8.2 and Laravel to a patched 12 or 13 release before taking 2.21.0. Existing 2.20.x legacy risks remain recorded in that release's dependency security policy."
},
"PKSA-b41t-wbzk-9q51": {
"github_advisory": "GHSA-f9x3-7vq9-cph6",
"affected_supported_majors": [
"10",
"11"
],
"review_by": "2026-11-07",
"reason": "Laravel 10.50.4 and 11.56.1, the supported minimums, contain the strict string TokenGuard checks (upstream 47781ee0 and 5056ca53), but the advisory feed still classifies every release below 12.68.0 as affected. Laravel 9, which has no fix, was removed from the supported constraint in Workflow 2.17.0; Laravel 9 applications stay on 2.16.x. A GitHub advisory-database correction adding 10.50.4 and 11.56.1 as patched is under Security review (#922). Other declared legacy risks, dependency alerts and exact per-major audits remain."
"11": {
"last_workflow_minor": "2.20",
"reason": "Laravel 11 is outside upstream security support. Workflow 2.21.0 removes this major; retain the existing 2.20.x application lock while upgrading PHP to at least 8.2 and Laravel to a patched 12 or 13 release before taking 2.21.0. Existing 2.20.x legacy risks remain recorded in that release's dependency security policy."
}
}
},
"accepted_advisories": {}
}
15 changes: 3 additions & 12 deletions resources/laravel-embedded-upgrade-contract.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,19 @@
"laravel": "^9.0|^10.0|^11.0|^12.0|^13.0"
},
"embedded_v2": {
"php": "^8.1",
"laravel": "^10.50.4|^11.56.1|^12.69.0|^13.30.0"
"php": "^8.2",
"laravel": "^12.69.0|^13.30.0"
},
"qualified_php_minors": ["8.1", "8.2", "8.3", "8.4"],
"qualified_php_minors": ["8.2", "8.3", "8.4"],
"laravel_minimum_php": {
"10.*": "8.1",
"11.*": "8.2",
"12.*": "8.2",
"13.*": "8.3"
}
},
"cells": [
{"php": "8.1", "laravel": "10.*"},
{"php": "8.2", "laravel": "10.*"},
{"php": "8.2", "laravel": "11.*"},
{"php": "8.2", "laravel": "12.*"},
{"php": "8.3", "laravel": "10.*"},
{"php": "8.3", "laravel": "11.*"},
{"php": "8.3", "laravel": "12.*"},
{"php": "8.3", "laravel": "13.*"},
{"php": "8.4", "laravel": "10.*"},
{"php": "8.4", "laravel": "11.*"},
{"php": "8.4", "laravel": "12.*"},
{"php": "8.4", "laravel": "13.*"}
]
Expand Down
69 changes: 62 additions & 7 deletions tests/Unit/V2/LaravelDependencySecurityPolicyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,25 +72,80 @@ public function testTheComposerConstraintRefusesRemovedLaravelMajors(): void
);
$constraint = $composer['require']['laravel/framework'];

$this->assertSame(['9'], array_map('strval', array_keys($policy['removed_majors'])));
$this->assertSame(['9', '10', '11'], array_map('strval', array_keys($policy['removed_majors'])));
$this->assertSame('2.16', $policy['removed_majors']['9']['last_workflow_minor']);
$this->assertSame('2.20', $policy['removed_majors']['10']['last_workflow_minor']);
$this->assertSame('2.20', $policy['removed_majors']['11']['last_workflow_minor']);

// 9.52.23 is the newest Laravel 9 release; its TokenGuard is unfixed.
foreach (['9.0.0', '9.52.17', '9.52.23', '9.99.99'] as $version) {
foreach ([
'9.0.0', '9.52.17', '9.52.23', '9.99.99',
'10.50.4', '10.99.99', '11.56.1', '11.99.99',
] as $version) {
$this->assertFalse(Semver::satisfies($version, $constraint), "{$version} must be refused");
}
foreach (['10.50.3', '11.56.0', '12.68.9', '13.29.9'] as $version) {
foreach (['12.68.9', '13.29.9'] as $version) {
$this->assertFalse(Semver::satisfies($version, $constraint), "{$version} is below its floor");
}
foreach ($policy['supported_majors'] as $major) {
$this->assertTrue(Semver::satisfies($major['minimum'], $constraint), $major['minimum']);
}

$this->assertArrayNotHasKey('9.*', $upgrade['supported_intersection']['authority']['laravel_minimum_php']);
$this->assertNotContains('9.*', array_column($upgrade['supported_intersection']['cells'], 'laravel'));
foreach ($policy['accepted_advisories'] as $advisory) {
$this->assertNotContains('9', $advisory['affected_supported_majors']);
foreach (['9.*', '10.*', '11.*'] as $removed) {
$this->assertArrayNotHasKey(
$removed,
$upgrade['supported_intersection']['authority']['laravel_minimum_php']
);
$this->assertNotContains($removed, array_column($upgrade['supported_intersection']['cells'], 'laravel'));
}
$this->assertSame([], $policy['accepted_advisories']);
}

public function testTheEmbeddedPhpFloorPreservesTheStableV1Authority(): void
{
$root = dirname(__DIR__, 3);
$composer = json_decode(
(string) file_get_contents($root . '/composer.json'),
true,
512,
JSON_THROW_ON_ERROR,
);
$upgrade = json_decode(
(string) file_get_contents($root . '/resources/laravel-embedded-upgrade-contract.json'),
true,
512,
JSON_THROW_ON_ERROR,
);
$authority = $upgrade['supported_intersection']['authority'];

foreach (['8.1.0', '8.1.33', '8.1.99'] as $version) {
$this->assertFalse(Semver::satisfies($version, $composer['require']['php']));
}
foreach (['8.2.0', '8.3.0', '8.4.0'] as $version) {
$this->assertTrue(Semver::satisfies($version, $composer['require']['php']));
}
$this->assertSame([
'php' => '^8.1',
'laravel' => '^9.0|^10.0|^11.0|^12.0|^13.0',
], $authority['stable_v1']);
$this->assertSame(['8.2', '8.3', '8.4'], $authority['qualified_php_minors']);
$this->assertSame([
[
'php' => '8.2',
'laravel' => '12.*',
],
[
'php' => '8.3',
'laravel' => '13.*',
],
], array_map(
static fn (string $laravel, string $php): array => [
'php' => $php,
'laravel' => $laravel,
],
array_keys($authority['laravel_minimum_php']),
array_values($authority['laravel_minimum_php']),
));
}

public function testRootComposerDependabotUpdatesCannotBeConfigured(): void
Expand Down
Loading