v1.2.1
inertia-sails v1.3.1
Security: Shared props no longer leak across requests
This release fixes a critical bug where user-specific shared props could leak between requests, causing one user's data (e.g., loggedInUser) to appear in another user's session — including unauthenticated visitors.
This affects all Boring Stack apps that call sails.inertia.share() in routes.before handlers, which is the standard pattern for passing logged-in user data to the frontend.
The bug
Three issues combined to create a data leak:
-
share()silently fell back to global storage — When called outside AsyncLocalStorage context,share()wrote toglobalSharedProps, a process-level singleton. That data persisted and bled into every subsequent request. -
flushShared()never cleaned global storage — Theglobalparameter defaulted tofalse, so stale data inglobalSharedPropswas never cleared between requests. -
Hook load order created a race condition — inertia-sails set up AsyncLocalStorage context in
routes.before, but other hooks (like the custom hook) also useroutes.beforeto callshare(). If another hook's handler ran first,share()was called before context existed, triggering the global fallback.
The fix
AsyncLocalStorage context is now injected as HTTP middleware during configure(), guaranteeing it exists before any hook's routes.before handler runs.
Request lifecycle (before):
routes.before handlers (race condition!) → router → controller
Request lifecycle (after):
cookieParser → session → ... → inertiaContext → routes.before handlers → router → controller
↑
AsyncLocalStorage context
is ready before any hook
calls share()
The fix uses Sails' configure() lifecycle phase — which runs for all hooks before any hook's initialize() — to inject an inertiaContext middleware into the HTTP stack before the router.
For socket requests (which bypass Express middleware and go through sails.router.route() directly), the existing routes.before handlers remain as a fallback with a guard that skips setup if context was already established by the HTTP middleware.
Breaking changes
share() no longer falls back to global storage
Previously, calling share() outside a request context silently wrote to globalSharedProps. Now it logs a warning and discards the value.
// This still works — called inside routes.before, controllers, helpers, etc.
sails.inertia.share('loggedInUser', user)
// This now logs a warning instead of silently leaking data
// Use shareGlobally() for truly global data like app name
sails.inertia.shareGlobally('appName', 'My App')If you see warnings like share('key') called outside request context, move the call into a routes.before handler, controller, or helper — anywhere inside the request lifecycle.
flushShared() always cleans global storage
The global parameter has been removed. flushShared() now always cleans both request-scoped and global storage to prevent stale data.
// Before
sails.inertia.flushShared('key', true) // had to pass true to clean global
sails.inertia.flushShared() // only cleaned request-scoped
// After
sails.inertia.flushShared('key') // cleans both request-scoped and global
sails.inertia.flushShared() // cleans both request-scoped and globallocal() no longer falls back to global storage
Same change as share() — use localGlobally() for global locals.
How to verify the fix
-
Update inertia-sails:
npm install inertia-sails@1.3.1
-
Start your app and log in as User A in one browser
-
Open the app in a different browser or incognito window (not logged in)
-
Verify the unauthenticated browser does not show User A's avatar, name, or any session data
-
Check server logs — there should be no
share() called outside request contextwarnings under normal operation
Additional fixes
- TypeScript: Fixed
AsyncLocalStorage.run()callback type error — wrapped the callback to satisfy the() => anysignature expected by therun()overload
References
Migration guide
No code changes required in your app. Just update the dependency:
npm install inertia-sails@1.3.1If you were calling flushShared() with the second global argument, remove it — global is now always cleaned:
// Before
sails.inertia.flushShared('key', true)
// After
sails.inertia.flushShared('key')Full Changelog: v1.2.0...v1.2.1