Skip to content

Commit 4427bb5

Browse files
committed
fix(audit-logs): include system events (null actor) in default org audit scope
SQL IN never matches NULL, so system/automated events inside org workspaces were hidden unless includeDeparted=true. The default scope now matches current members OR null-actor rows, still inside the org boundary.
1 parent 720b203 commit 4427bb5

2 files changed

Lines changed: 21 additions & 13 deletions

File tree

apps/sim/app/api/v1/audit-logs/query.test.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,11 @@ describe('buildOrgScopeCondition', () => {
7575
expectOrgLevelCondition(orgLevel, ORG_ID)
7676

7777
expect(actorFilter).toMatchObject({
78-
type: 'inArray',
79-
column: 'actorId',
80-
values: MEMBER_IDS,
78+
type: 'or',
79+
conditions: [
80+
expect.objectContaining({ type: 'inArray', column: 'actorId', values: MEMBER_IDS }),
81+
expect.objectContaining({ type: 'isNull', column: 'actorId' }),
82+
],
8183
})
8284
})
8385

@@ -130,13 +132,15 @@ describe('buildOrgScopeCondition', () => {
130132
const [orgLevel, actorFilter] = condition.conditions!
131133
expectOrgLevelCondition(orgLevel, ORG_ID)
132134
expect(actorFilter).toMatchObject({
133-
type: 'inArray',
134-
column: 'actorId',
135-
values: MEMBER_IDS,
135+
type: 'or',
136+
conditions: [
137+
expect.objectContaining({ type: 'inArray', column: 'actorId', values: MEMBER_IDS }),
138+
expect.objectContaining({ type: 'isNull', column: 'actorId' }),
139+
],
136140
})
137141
})
138142

139-
it('matches nothing when the org has no current members and includeDeparted is false', () => {
143+
it('only matches system events when the org has no current members', () => {
140144
const condition = asCondition(
141145
buildOrgScopeCondition({
142146
organizationId: ORG_ID,
@@ -146,7 +150,9 @@ describe('buildOrgScopeCondition', () => {
146150
})
147151
)
148152

149-
expect(condition.strings?.join('')).toBe('1 = 0')
153+
expect(condition.type).toBe('and')
154+
const [, actorFilter] = condition.conditions!
155+
expect(actorFilter).toMatchObject({ type: 'isNull', column: 'actorId' })
150156
})
151157
})
152158

apps/sim/app/api/v1/audit-logs/query.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ export interface OrgScopeParams {
9292
* rows in org-attached workspaces, plus org-level rows (`workspace_id IS
9393
* NULL`) tied to the org via `metadata.organizationId` or the organization
9494
* resource itself. Actor membership is never a standalone boundary — when
95-
* `includeDeparted` is false it only narrows the org scope.
95+
* `includeDeparted` is false it only narrows the org scope to current members
96+
* and system events (null actor).
9697
*/
9798
export function buildOrgScopeCondition(params: OrgScopeParams): SQL<unknown> {
9899
const { organizationId, orgWorkspaceIds, orgMemberIds, includeDeparted } = params
@@ -117,11 +118,12 @@ export function buildOrgScopeCondition(params: OrgScopeParams): SQL<unknown> {
117118
return orgScope
118119
}
119120

120-
if (orgMemberIds.length === 0) {
121-
return sql`1 = 0`
122-
}
121+
const currentActorCondition =
122+
orgMemberIds.length > 0
123+
? or(inArray(auditLog.actorId, orgMemberIds), isNull(auditLog.actorId))!
124+
: isNull(auditLog.actorId)
123125

124-
return and(orgScope, inArray(auditLog.actorId, orgMemberIds))!
126+
return and(orgScope, currentActorCondition)!
125127
}
126128

127129
function buildCursorCondition(cursor: string): SQL<unknown> | null {

0 commit comments

Comments
 (0)