fix(agent): stop the audit store's queries from adopting a host's CLS transaction - #1842
Open
bexchauveto wants to merge 1 commit into
Open
fix(agent): stop the audit store's queries from adopting a host's CLS transaction#1842bexchauveto wants to merge 1 commit into
bexchauveto wants to merge 1 commit into
Conversation
… transaction Sequelize.useCLS(ns) sets Sequelize._cls, a class-level static shared by every Sequelize instance in the same loaded copy of the package — including the audit store's own, otherwise-unrelated connection. Any query issued without an explicit `transaction` reads that static to decide whether to adopt an ambient transaction, so a host that uses CLS for its own request-scoped transactions would have every audit query silently run against the host's connection and inside the host's transaction instead of the audit store's own pool: if the two databases differ, the write 404s inside the host's transaction and aborts it; if they're the same database, a pending row rolls back with the host transaction, defeating the "pending row is evidence a write was attempted" guarantee the write protocol is built on. Every query the store issues now passes `transaction: null` explicitly, opting out of the CLS lookup unconditionally. Also threads a migration's own transaction through `assertOwnsTable`'s `describeTable` probe, which omitted any transaction at all. Found while enabling auditTrail on a CLS-using host.
1 new issue
|
|
Coverage Impact This PR will not change total coverage. Modified Files with Diff Coverage (2)
🛟 Help
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
auditTrailon a CLS-using host:Sequelize.useCLS(ns)setsSequelize._cls, a class-level static shared by everySequelizeinstance in the same loaded copy of the package — including the audit store's own, otherwise-unrelated connection. Any query issued without an explicittransactionreads that static (options.transaction === void 0 && Sequelize._cls) to decide whether to adopt an ambient transaction. A host using CLS for its own request-scoped transactions therefore had every audit-store query silently run against the host's connection, inside the host's transaction, instead of the audit store's own pool.Sequelize.prototype.query()) and reproduced with a fake CLS namespace causing a liveinsertPending/confirmcall to reach for a bogus hijacked connection instead of its own.critical: false); if they're the same database, apendingrow rolls back with the host's transaction, defeating the "a pending row is evidence a write was attempted" guarantee the write protocol is built on.transaction: nullexplicitly, opting out of the CLS lookup unconditionally —insertPending,insertPendingBatch,confirm,listByRecord,countByRecord,listDistinctUsers,listByCorrelation(s), andauthenticate. Also threads a migration's own transaction throughassertOwnsTable'sdescribeTableprobe, which previously passed no transaction option at all (same class of gap, narrower window).previousValuesunredacted) was investigated and does not reproduce:buildRecordininstrument.tsalready appliesredactValuesunconditionally before returning, including for the Before-Update/Before-Delete pending inserts — verified against both current source and the published1.96.0dist bundle.Test plan
yarn workspace @forestadmin/agent test— 1439 passing (was 1438; one new regression test added)yarn workspace @forestadmin/agent lint— 0 errorssql-store.test.ts) sets a fakeSequelize._clsnamespace with a transaction whose.connectiongetter is spied, then confirms a pending audit row and asserts the getter was never called — verified this test fails without the fix and passes with it.auditTrailalongside a host that callsSequelize.useCLS(...)for its own transactions, and confirm an audited write inside a host transaction no longer touches the host's connection.Note
Stop audit store queries from adopting host CLS transactions
{ transaction: null }to every Sequelize model operation in sql-store.ts (create,bulkCreate,update,findAll,count, andauthenticate) so the store never adopts an ambient CLS transaction set by the host process.context.transactionthroughassertOwnsTable→columnNamesin migrations.ts so the migration ownership/idempotency check runs inside the migration's own transaction instead of outside it.Reflect.set(Sequelize, "_cls", ...)and assertsstore.confirmdoes not touch the hijacked transaction's connection getter.context.transactionis provided.Macroscope summarized 845b909.