-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Security Assistant] Adds audit logging to knowledge base entry chang…
…es (#203349)
- Loading branch information
1 parent
b9bac16
commit 84a2d40
Showing
10 changed files
with
366 additions
and
10 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
181 changes: 181 additions & 0 deletions
181
...ns/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/audit_events.test.ts
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import { | ||
knowledgeBaseAuditEvent, | ||
KnowledgeBaseAuditAction, | ||
AUDIT_OUTCOME, | ||
AUDIT_CATEGORY, | ||
AUDIT_TYPE, | ||
} from './audit_events'; | ||
|
||
describe('knowledgeBaseAuditEvent', () => { | ||
it('should generate a success event with id', () => { | ||
const event = knowledgeBaseAuditEvent({ | ||
action: KnowledgeBaseAuditAction.CREATE, | ||
id: '123', | ||
outcome: AUDIT_OUTCOME.SUCCESS, | ||
}); | ||
|
||
expect(event).toEqual({ | ||
message: 'User has created knowledge base entry [id=123]', | ||
event: { | ||
action: KnowledgeBaseAuditAction.CREATE, | ||
category: [AUDIT_CATEGORY.DATABASE], | ||
type: [AUDIT_TYPE.CREATION], | ||
outcome: AUDIT_OUTCOME.SUCCESS, | ||
}, | ||
}); | ||
}); | ||
it('should generate a success event with name', () => { | ||
const event = knowledgeBaseAuditEvent({ | ||
action: KnowledgeBaseAuditAction.CREATE, | ||
name: 'My document', | ||
outcome: AUDIT_OUTCOME.SUCCESS, | ||
}); | ||
|
||
expect(event).toEqual({ | ||
message: 'User has created knowledge base entry [name="My document"]', | ||
event: { | ||
action: KnowledgeBaseAuditAction.CREATE, | ||
category: [AUDIT_CATEGORY.DATABASE], | ||
type: [AUDIT_TYPE.CREATION], | ||
outcome: AUDIT_OUTCOME.SUCCESS, | ||
}, | ||
}); | ||
}); | ||
it('should generate a success event with name and id', () => { | ||
const event = knowledgeBaseAuditEvent({ | ||
action: KnowledgeBaseAuditAction.CREATE, | ||
name: 'My document', | ||
id: '123', | ||
outcome: AUDIT_OUTCOME.SUCCESS, | ||
}); | ||
|
||
expect(event).toEqual({ | ||
message: 'User has created knowledge base entry [id=123, name="My document"]', | ||
event: { | ||
action: KnowledgeBaseAuditAction.CREATE, | ||
category: [AUDIT_CATEGORY.DATABASE], | ||
type: [AUDIT_TYPE.CREATION], | ||
outcome: AUDIT_OUTCOME.SUCCESS, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should generate a success event without id or name', () => { | ||
const event = knowledgeBaseAuditEvent({ | ||
action: KnowledgeBaseAuditAction.CREATE, | ||
outcome: AUDIT_OUTCOME.SUCCESS, | ||
}); | ||
|
||
expect(event).toEqual({ | ||
message: 'User has created a knowledge base entry', | ||
event: { | ||
action: KnowledgeBaseAuditAction.CREATE, | ||
category: [AUDIT_CATEGORY.DATABASE], | ||
type: [AUDIT_TYPE.CREATION], | ||
outcome: AUDIT_OUTCOME.SUCCESS, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should generate a failure event with an error', () => { | ||
const error = new Error('Test error'); | ||
const event = knowledgeBaseAuditEvent({ | ||
action: KnowledgeBaseAuditAction.CREATE, | ||
id: '456', | ||
error, | ||
}); | ||
|
||
expect(event).toEqual({ | ||
message: 'Failed attempt to create knowledge base entry [id=456]', | ||
event: { | ||
action: KnowledgeBaseAuditAction.CREATE, | ||
category: [AUDIT_CATEGORY.DATABASE], | ||
type: [AUDIT_TYPE.CREATION], | ||
outcome: AUDIT_OUTCOME.FAILURE, | ||
}, | ||
error: { | ||
code: error.name, | ||
message: error.message, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should handle unknown outcome', () => { | ||
const event = knowledgeBaseAuditEvent({ | ||
action: KnowledgeBaseAuditAction.CREATE, | ||
id: '789', | ||
outcome: AUDIT_OUTCOME.UNKNOWN, | ||
}); | ||
|
||
expect(event).toEqual({ | ||
message: 'User is creating knowledge base entry [id=789]', | ||
event: { | ||
action: KnowledgeBaseAuditAction.CREATE, | ||
category: [AUDIT_CATEGORY.DATABASE], | ||
type: [AUDIT_TYPE.CREATION], | ||
outcome: AUDIT_OUTCOME.UNKNOWN, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should handle update action', () => { | ||
const event = knowledgeBaseAuditEvent({ | ||
action: KnowledgeBaseAuditAction.UPDATE, | ||
id: '123', | ||
outcome: AUDIT_OUTCOME.SUCCESS, | ||
}); | ||
|
||
expect(event).toEqual({ | ||
message: 'User has updated knowledge base entry [id=123]', | ||
event: { | ||
action: KnowledgeBaseAuditAction.UPDATE, | ||
category: [AUDIT_CATEGORY.DATABASE], | ||
type: [AUDIT_TYPE.CHANGE], | ||
outcome: AUDIT_OUTCOME.SUCCESS, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should handle delete action', () => { | ||
const event = knowledgeBaseAuditEvent({ | ||
action: KnowledgeBaseAuditAction.DELETE, | ||
id: '123', | ||
}); | ||
|
||
expect(event).toEqual({ | ||
message: 'User has deleted knowledge base entry [id=123]', | ||
event: { | ||
action: KnowledgeBaseAuditAction.DELETE, | ||
category: [AUDIT_CATEGORY.DATABASE], | ||
type: [AUDIT_TYPE.DELETION], | ||
outcome: AUDIT_OUTCOME.SUCCESS, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should default to success if outcome is not provided and no error exists', () => { | ||
const event = knowledgeBaseAuditEvent({ | ||
action: KnowledgeBaseAuditAction.CREATE, | ||
}); | ||
|
||
expect(event.event?.outcome).toBe(AUDIT_OUTCOME.SUCCESS); | ||
}); | ||
|
||
it('should prioritize error outcome over provided outcome', () => { | ||
const error = new Error('Error with priority'); | ||
const event = knowledgeBaseAuditEvent({ | ||
action: KnowledgeBaseAuditAction.CREATE, | ||
outcome: AUDIT_OUTCOME.SUCCESS, | ||
error, | ||
}); | ||
|
||
expect(event.event?.outcome).toBe(AUDIT_OUTCOME.FAILURE); | ||
}); | ||
}); |
94 changes: 94 additions & 0 deletions
94
...plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/audit_events.ts
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import { EcsEvent } from '@kbn/core/server'; | ||
import { AuditEvent } from '@kbn/security-plugin/server'; | ||
import { ArrayElement } from '@kbn/utility-types'; | ||
|
||
export enum AUDIT_TYPE { | ||
CHANGE = 'change', | ||
DELETION = 'deletion', | ||
ACCESS = 'access', | ||
CREATION = 'creation', | ||
} | ||
|
||
export enum AUDIT_CATEGORY { | ||
AUTHENTICATION = 'authentication', | ||
DATABASE = 'database', | ||
WEB = 'web', | ||
} | ||
|
||
export enum AUDIT_OUTCOME { | ||
FAILURE = 'failure', | ||
SUCCESS = 'success', | ||
UNKNOWN = 'unknown', | ||
} | ||
|
||
export enum KnowledgeBaseAuditAction { | ||
CREATE = 'knowledge_base_entry_create', | ||
UPDATE = 'knowledge_base_entry_update', | ||
DELETE = 'knowledge_base_entry_delete', | ||
} | ||
|
||
type VerbsTuple = [string, string, string]; | ||
const knowledgeBaseEventVerbs: Record<KnowledgeBaseAuditAction, VerbsTuple> = { | ||
knowledge_base_entry_create: ['create', 'creating', 'created'], | ||
knowledge_base_entry_update: ['update', 'updating', 'updated'], | ||
knowledge_base_entry_delete: ['delete', 'deleting', 'deleted'], | ||
}; | ||
|
||
const knowledgeBaseEventTypes: Record<KnowledgeBaseAuditAction, ArrayElement<EcsEvent['type']>> = { | ||
knowledge_base_entry_create: AUDIT_TYPE.CREATION, | ||
knowledge_base_entry_update: AUDIT_TYPE.CHANGE, | ||
knowledge_base_entry_delete: AUDIT_TYPE.DELETION, | ||
}; | ||
|
||
export interface KnowledgeBaseAuditEventParams { | ||
action: KnowledgeBaseAuditAction; | ||
error?: Error; | ||
id?: string; | ||
name?: string; | ||
outcome?: EcsEvent['outcome']; | ||
} | ||
|
||
export function knowledgeBaseAuditEvent({ | ||
action, | ||
error, | ||
id, | ||
name, | ||
outcome, | ||
}: KnowledgeBaseAuditEventParams): AuditEvent { | ||
let doc = 'a knowledge base entry'; | ||
if (id && name) { | ||
doc = `knowledge base entry [id=${id}, name="${name}"]`; | ||
} else if (id) { | ||
doc = `knowledge base entry [id=${id}]`; | ||
} else if (name) { | ||
doc = `knowledge base entry [name="${name}"]`; | ||
} | ||
const [present, progressive, past] = knowledgeBaseEventVerbs[action]; | ||
const message = error | ||
? `Failed attempt to ${present} ${doc}` | ||
: outcome === 'unknown' | ||
? `User is ${progressive} ${doc}` | ||
: `User has ${past} ${doc}`; | ||
const type = knowledgeBaseEventTypes[action]; | ||
|
||
return { | ||
message, | ||
event: { | ||
action, | ||
category: [AUDIT_CATEGORY.DATABASE], | ||
type: type ? [type] : undefined, | ||
outcome: error ? AUDIT_OUTCOME.FAILURE : outcome ?? AUDIT_OUTCOME.SUCCESS, | ||
}, | ||
error: error && { | ||
code: error.name, | ||
message: error.message, | ||
}, | ||
}; | ||
} |
This file contains 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
Oops, something went wrong.