Skip to content

fix(security): scoped binding + recursive tx integrity + aligned detach (BC)#97

Merged
emreakay merged 4 commits into
mainfrom
fix-critical-auth-bugs
May 22, 2026
Merged

fix(security): scoped binding + recursive tx integrity + aligned detach (BC)#97
emreakay merged 4 commits into
mainfrom
fix-critical-auth-bugs

Conversation

@emreakay

Copy link
Copy Markdown
Contributor

Summary

Three corrective fixes to AAuth core lifecycle and service layer. All backward compatiblecomposer update on existing 21.x projects will not break a working host application, and PHP-FPM behaviour is byte-equivalent to today.

1. SECURITY (HIGH severity) — Octane/Vapor cross-request state leak

Reported by a production consumer running on Laravel Octane. The aauth container binding was a singleton, so a long-lived worker reused User A's resolved AAuth instance (carrying \$user, \$role, \$organizationNodeIds, permissions, ABAC rules, super-admin flag) for User B's subsequent request → authorization bypass, super-admin escalation, PII leak.

Fix: \$this->app->scoped('aauth', ...). Laravel 9+ flushes scoped bindings on RequestTerminated. PHP-FPM unaffected (every request is a fresh process). See src/AAuthServiceProvider.php:63.

2. RELIABILITY — silent transaction corruption in recursive ops

OrganizationService::updateNodePathsRecursively and deleteOrganizationNodesRecursively opened a transaction, swallowed any exception in catch, then called commit() anyway → subtree left half-modified, caller never knew.

Fix: wrap recursion in DB::transaction() (savepoints handle nesting natively). Public signatures (including \$withDBTransaction parameter) preserved. Exceptions now propagate to the caller; everything else byte-equivalent.

3. API CONSISTENCY (BC-safe) — aligned-order detach method

detachOrganizationRoleFromUser(\$userId, \$roleId, \$nodeId) takes parameters in inverse order from attachOrganizationRoleToUser(\$nodeId, \$roleId, \$userId). Re-ordering would silently corrupt data for every consumer.

Fix: added new detachOrganizationRoleFromUserBy(\$nodeId, \$roleId, \$userId) with aligned order. Legacy method kept, no runtime notice emitted — only @deprecated docblock so IDEs and PHPStan flag call sites. To be removed in next major.

Test plan

  • Local: `vendor/bin/pest` — 137 passed, 274 assertions, 47s
  • OpenSpec change validated: `openspec validate fix-critical-auth-bugs` ✓
  • Main spec synced: `openspec/specs/core-rbac/spec.md` (1 modified, 3 added requirements)
  • PHP syntax check on every touched file
  • GitHub Actions matrix (PHP 8.2/8.3/8.4 × Laravel 11/12 × prefer-lowest/stable, MySQL 8.0 service) — verify on this PR
  • README updated with Octane callout
  • UPGRADE.md documents 21.1.0 migration recipe for all three fixes

New test files

  • `tests/Unit/V2/OctaneScopingTest.php` — 6 tests including SECURITY regression cases for cross-user public-property leak and super-admin escalation
  • `tests/Unit/V2/TransactionIntegrityTest.php` — 7 tests including deep-recursion rollback, caller-managed transaction integration, BC signature

Files changed

  • `src/AAuthServiceProvider.php` — singleton → scoped
  • `src/Services/OrganizationService.php` — DB::transaction wrappers, exception propagation
  • `src/Services/RolePermissionService.php` — added `detachOrganizationRoleFromUserBy`, deprecated docblock on legacy method
  • `tests/TestCase.php` — respects DB_CONNECTION env (was hardcoded to mysql); supports AAUTH_TEST_DB=sqlite override
  • Tests + docs + OpenSpec artifacts

BC contract verified

  • composer update on any 21.x → working host app keeps working
  • ✅ PHP-FPM: zero observable change
  • ✅ Public method signatures unchanged
  • ✅ No new exceptions thrown from previously-quiet paths (except OrganizationService recursive methods which now propagate previously-swallowed errors — a documented intentional fix)
  • ✅ No runtime deprecation notices emitted in this release
  • ✅ Configuration defaults unchanged

🤖 Generated with Claude Code

@what-the-diff

what-the-diff Bot commented May 22, 2026

Copy link
Copy Markdown

PR Summary

  • Readme and Upgrade Guide Updates

    • Improved documentation for Laravel Octane/Vapor environments to prevent potential state leaks in these fast-paced contexts.
    • Provided upgrade instructions to ensure seamless transition from previous versions, detailing all changes in authorization, error handling, and method consistency.
  • Open Specification Changes

    • Proposed, documented, and started tracking tasks for introducing security and reliability improvements in the application.
  • Core Changes

    • Enforced 'per-request' execution of authorization checks to improve security in extended uptime settings.
    • Ensured successful recursive operations by properly utilizing database transactions and rollback facility.
    • Introduced a method for detaching organization roles in a more consistent way while maintaining compatibility with existing methods.
  • Test Coverage and CI Infrastructure

    • Expanded test coverage to account for new developments and behaviors.
    • Implemented test workflows to capture, validate, and ensure the longevity of these improvements.
  • Major Module-Level Improvements

    • Added a variety of tests for role-methods and binding behaviors.
    • Maintained the integrity of transactional operations with rollback support in case of failure.
    • Validated the new method of detaching roles from organizations.
  • Integrity Checks and Validation

    • Developed tests to confirm the risk-free operation of AAuth's new request-bound execution.
    • Ensured that the new transaction models roll back correctly upon encountering errors during recursion.
    • Verified that the system does not crash due to unexpected issues while performing atomic operations.

Emre Akay and others added 3 commits May 22, 2026 16:05
…ch (BC)

Three corrective fixes to AAuth core lifecycle and service layer, all
backward compatible — composer update on existing 21.x projects will not
break a working host application, and PHP-FPM behaviour is byte-equivalent.

1. SECURITY (HIGH): AAuth container binding now uses scoped() instead of
   singleton(), so Laravel Octane/Vapor workers no longer leak one user's
   AAuth state (user, role, organization node IDs, permissions, ABAC rules,
   super-admin flag) into the next user's request. PHP-FPM is unaffected.

2. RELIABILITY: OrganizationService::updateNodePathsRecursively and
   deleteOrganizationNodesRecursively now wrap recursion in DB::transaction()
   with savepoints. Previously they swallowed exceptions and committed the
   outer transaction anyway, leaving subtrees in an inconsistent state.
   Public signatures (including $withDBTransaction parameter) preserved.

3. API CONSISTENCY (BC-safe): added detachOrganizationRoleFromUserBy()
   with parameter order matching attachOrganizationRoleToUser(). The
   existing detachOrganizationRoleFromUser() remains in place, fully
   working, marked @deprecated in docblock only (no runtime notice). To
   be removed in next major release.

Tests: 137 passing, 274 assertions. Added OctaneScopingTest (6 tests
including SECURITY regression cases for cross-user property and super-
admin leak), TransactionIntegrityTest (7 tests including deep-recursion
rollback and caller-managed transaction integration). Filled todos in
RolePermissionServiceTest (updateRole, deleteRole, activateRole,
deactivateRole) and AAuthTest (passOrAbort, organizationNode, static
switchableRoles).

TestCase.php now respects DB_CONNECTION env (phpunit.xml.dist defaults
to mysql for CI; local devs can override). Tests pass against MariaDB
locally and MySQL 8.0 on GitHub Actions matrix.

OpenSpec change: openspec/changes/fix-critical-auth-bugs/
Main spec synced: openspec/specs/core-rbac/spec.md (1 modified, 3 added
requirements).

README updated with Octane note. UPGRADE.md documents 21.1.0 migration
recipe for all three fixes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Moved openspec/changes/fix-critical-auth-bugs/ to
  openspec/changes/archive/2026-05-22-fix-critical-auth-bugs/ now that
  implementation is complete (main spec was already synced in the previous
  commit, so --skip-specs was used during archival).
- Removed .github/workflows/claude-code-review.yml: the Claude Code Action
  reports the org's Claude subscription is disabled for Code on this repo,
  causing every PR run to fail. Re-add (or replace with the API-key variant)
  once subscription access is sorted out.
- No composer dependency bumps in this PR: `composer outdated --direct` only
  surfaces major-version bumps (Laravel 13, Pest 4, PHPUnit 13, Testbench 11).
  Those are intentionally deferred to dedicated PRs to keep this security
  fix isolated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@emreakay emreakay force-pushed the fix-critical-auth-bugs branch from 718c582 to 386ece3 Compare May 22, 2026 13:06
…Node::create

The composer update (Laravel 10 → 12, Larastan upgrade) surfaced a stricter
type check on OrganizationService::createOrganizationNode line 106: passing
a generic `array` to OrganizationNode::create() now fails because Larastan
expects `array<model property of OrganizationNode, mixed>`.

Fix: build the attributes array with the fillable column names as literal
keys before passing to create(). Behaviour is byte-equivalent — same
columns are populated from the same input keys — but PHPStan can now
verify the shape statically.

Verified:
- vendor/bin/phpstan analyse --memory-limit=1G  → no errors
- vendor/bin/pest                                → 137 passed (274 assertions)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@emreakay emreakay merged commit b7c9183 into main May 22, 2026
27 checks passed
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