0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-10 22:22:45 -05:00

fix(core): mask password in audit log (#3130)

This commit is contained in:
simeng-li 2023-02-16 16:45:34 +08:00 committed by GitHub
parent 016833905d
commit 3b0bee717a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 59 additions and 1 deletions

View file

@ -0,0 +1,5 @@
---
"@logto/core": minor
---
- mask sensitive password value in audit logs

View file

@ -122,6 +122,44 @@ describe('koaAuditLog middleware', () => {
expect(insertLog).not.toBeCalled();
});
it('should filter password sensitive data in log', async () => {
// @ts-expect-error for testing
const ctx: WithLogContext<ReturnType<typeof createContextWithRouteParameters>> = {
...createContextWithRouteParameters({ headers: { 'user-agent': userAgent } }),
};
ctx.request.ip = ip;
const additionalMockPayload = {
password: '123456',
interaction: { profile: { password: 123_456 } },
};
const maskedAdditionalMockPayload = {
password: '******',
interaction: { profile: { password: '******' } },
};
const next = async () => {
const log = ctx.createLog(logKey);
log.append(mockPayload);
log.append(additionalMockPayload);
};
await koaLog(queries)(ctx, next);
expect(insertLog).toBeCalledWith({
id: nanoIdMock,
key: logKey,
payload: {
...mockPayload,
...maskedAdditionalMockPayload,
key: logKey,
result: LogResult.Success,
ip,
userAgent,
},
});
});
describe('should insert an error log with the error message when next() throws an error', () => {
it('should log with error message when next throws a normal Error', async () => {
// @ts-expect-error for testing

View file

@ -8,6 +8,21 @@ import type { IRouterParamContext } from 'koa-router';
import RequestError from '#src/errors/RequestError/index.js';
import type Queries from '#src/tenants/Queries.js';
const isRecord = (value: unknown): value is Record<string, unknown> =>
typeof value === 'object' && value !== null && !Array.isArray(value);
const filterSensitiveData = (data: Record<string, unknown>): Record<string, unknown> => {
return Object.fromEntries(
Object.entries(data).map(([key, value]) => {
if (isRecord(value)) {
return [key, filterSensitiveData(value)];
}
return [key, key === 'password' ? '******' : value];
})
);
};
const removeUndefinedKeys = (object: Record<string, unknown>) =>
Object.fromEntries(Object.entries(object).filter(([, value]) => value !== undefined));
@ -33,7 +48,7 @@ export class LogEntry {
append(data: Readonly<LogPayload>) {
this.payload = {
...this.payload,
...removeUndefinedKeys(data),
...filterSensitiveData(removeUndefinedKeys(data)),
};
}
}