Skip to content

Commit 1534f62

Browse files
authored
fix(lib-dynamodb): fix use of log filters in conjunction with DynamDBDocumentClient (#4249)
* fix(lib-dynamodb): fix use of log filters in conjunction with DynamoDBDocumentClient * fix(lib-dynamodb): add tests in middleware-logger
1 parent 8cf39f6 commit 1534f62

File tree

5 files changed

+112
-14
lines changed

5 files changed

+112
-14
lines changed

Diff for: lib/lib-dynamodb/src/baseCommand/DynamoDBDocumentClientCommand.spec.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class AnyCommand extends DynamoDBDocumentClientCommand<{}, {}, {}, {}, {}> {
1414
protected readonly clientCommand = {
1515
middlewareStack: {
1616
argCaptor: this.argCaptor,
17-
add(fn, config) {
17+
addRelativeTo(fn, config) {
1818
this.argCaptor.push([fn, config]);
1919
},
2020
},
@@ -41,7 +41,8 @@ describe("DynamoDBDocumentClientCommand", () => {
4141
expect(options).toEqual({
4242
name: "DocumentMarshall",
4343
override: true,
44-
step: "initialize",
44+
relation: "before",
45+
toMiddleware: "serializerMiddleware",
4546
});
4647
}
4748
{
@@ -50,7 +51,8 @@ describe("DynamoDBDocumentClientCommand", () => {
5051
expect(options).toEqual({
5152
name: "DocumentUnmarshall",
5253
override: true,
53-
step: "deserialize",
54+
relation: "before",
55+
toMiddleware: "deserializerMiddleware",
5456
});
5557
}
5658
});

Diff for: lib/lib-dynamodb/src/baseCommand/DynamoDBDocumentClientCommand.ts

+36-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
DeserializeHandler,
44
DeserializeHandlerArguments,
55
DeserializeHandlerOutput,
6+
HandlerExecutionContext,
67
InitializeHandler,
78
InitializeHandlerArguments,
89
InitializeHandlerOutput,
@@ -29,35 +30,64 @@ export abstract class DynamoDBDocumentClientCommand<
2930

3031
public abstract middlewareStack: MiddlewareStack<Input | BaseInput, Output | BaseOutput>;
3132

33+
private static defaultLogFilterOverrides = {
34+
overrideInputFilterSensitiveLog(...args: any[]) {},
35+
overrideOutputFilterSensitiveLog(...args: any[]) {},
36+
};
37+
3238
protected addMarshallingMiddleware(configuration: DynamoDBDocumentClientResolvedConfig): void {
3339
const { marshallOptions, unmarshallOptions } = configuration.translateConfig || {};
3440

35-
this.clientCommand.middlewareStack.add(
36-
(next: InitializeHandler<Input | BaseInput, Output | BaseOutput>) =>
41+
this.clientCommand.middlewareStack.addRelativeTo(
42+
(next: InitializeHandler<Input | BaseInput, Output | BaseOutput>, context: HandlerExecutionContext) =>
3743
async (
3844
args: InitializeHandlerArguments<Input | BaseInput>
3945
): Promise<InitializeHandlerOutput<Output | BaseOutput>> => {
4046
args.input = marshallInput(this.input, this.inputKeyNodes, marshallOptions);
47+
context.dynamoDbDocumentClientOptions =
48+
context.dynamoDbDocumentClientOptions || DynamoDBDocumentClientCommand.defaultLogFilterOverrides;
49+
50+
const input = args.input;
51+
context.dynamoDbDocumentClientOptions.overrideInputFilterSensitiveLog = () => {
52+
return context.inputFilterSensitiveLog?.(input);
53+
};
4154
return next(args);
4255
},
4356
{
4457
name: "DocumentMarshall",
45-
step: "initialize",
58+
relation: "before",
59+
toMiddleware: "serializerMiddleware",
4660
override: true,
4761
}
4862
);
49-
this.clientCommand.middlewareStack.add(
50-
(next: DeserializeHandler<Input | BaseInput, Output | BaseOutput>) =>
63+
this.clientCommand.middlewareStack.addRelativeTo(
64+
(next: DeserializeHandler<Input | BaseInput, Output | BaseOutput>, context: HandlerExecutionContext) =>
5165
async (
5266
args: DeserializeHandlerArguments<Input | BaseInput>
5367
): Promise<DeserializeHandlerOutput<Output | BaseOutput>> => {
5468
const deserialized = await next(args);
69+
70+
/**
71+
* The original filter function is based on the shape of the
72+
* base DynamoDB type, whereas the returned output will be
73+
* unmarshalled. Therefore the filter log needs to be modified
74+
* to act on the original output structure.
75+
*/
76+
const output = deserialized.output;
77+
context.dynamoDbDocumentClientOptions =
78+
context.dynamoDbDocumentClientOptions || DynamoDBDocumentClientCommand.defaultLogFilterOverrides;
79+
80+
context.dynamoDbDocumentClientOptions.overrideOutputFilterSensitiveLog = () => {
81+
return context.outputFilterSensitiveLog?.(output);
82+
};
83+
5584
deserialized.output = unmarshallOutput(deserialized.output, this.outputKeyNodes, unmarshallOptions);
5685
return deserialized;
5786
},
5887
{
5988
name: "DocumentUnmarshall",
60-
step: "deserialize",
89+
relation: "before",
90+
toMiddleware: "deserializerMiddleware",
6191
override: true,
6292
}
6393
);

Diff for: packages/middleware-logger/src/loggerMiddleware.spec.ts

+50-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Logger, MiddlewareStack } from "@aws-sdk/types";
1+
import { HandlerExecutionContext, Logger, MiddlewareStack } from "@aws-sdk/types";
22

33
import { getLoggerPlugin, loggerMiddleware, loggerMiddlewareOptions } from "./loggerMiddleware";
44

@@ -114,6 +114,55 @@ describe("loggerMiddleware", () => {
114114
});
115115
});
116116

117+
it("should use override log filters for DynamoDBDocumentClient if present", async () => {
118+
mockNext.mockResolvedValueOnce(mockResponse);
119+
120+
const logger = { info: jest.fn() } as unknown as Logger;
121+
const clientName = "mockClientName";
122+
const commandName = "mockCommandName";
123+
124+
const mockInputLog = { inputKey: "inputKey", inputSensitiveKey: "SENSITIVE_VALUE" };
125+
const inputFilterSensitiveLog = jest.fn().mockReturnValueOnce(mockInputLog);
126+
const mockOutputLog = { outputKey: "outputKey", outputSensitiveKey: "SENSITIVE_VALUE" };
127+
const outputFilterSensitiveLog = jest.fn().mockReturnValueOnce(mockOutputLog);
128+
129+
const context: HandlerExecutionContext = {
130+
logger,
131+
clientName,
132+
commandName,
133+
dynamoDbDocumentClientOptions: {
134+
overrideInputFilterSensitiveLog: inputFilterSensitiveLog,
135+
overrideOutputFilterSensitiveLog: outputFilterSensitiveLog,
136+
},
137+
inputFilterSensitiveLog() {
138+
throw new Error("should not be called");
139+
},
140+
outputFilterSensitiveLog() {
141+
throw new Error("should not be called");
142+
},
143+
};
144+
145+
const response = await loggerMiddleware()(mockNext, context)(mockArgs);
146+
expect(mockNext).toHaveBeenCalledTimes(1);
147+
expect(response).toStrictEqual(mockResponse);
148+
149+
expect(inputFilterSensitiveLog).toHaveBeenCalledTimes(1);
150+
expect(inputFilterSensitiveLog).toHaveBeenCalledWith(mockArgs.input);
151+
152+
const { $metadata, ...outputWithoutMetadata } = mockOutput;
153+
expect(outputFilterSensitiveLog).toHaveBeenCalledTimes(1);
154+
expect(outputFilterSensitiveLog).toHaveBeenCalledWith(outputWithoutMetadata);
155+
156+
expect(logger.info).toHaveBeenCalledTimes(1);
157+
expect(logger.info).toHaveBeenCalledWith({
158+
clientName,
159+
commandName,
160+
input: mockInputLog,
161+
output: mockOutputLog,
162+
metadata: $metadata,
163+
});
164+
});
165+
117166
it("header x-amzn-request-id as requestId if x-amzn-requestid is not present", async () => {
118167
const requestIdBackup = "requestIdBackup";
119168
const customResponse = {

Diff for: packages/middleware-logger/src/loggerMiddleware.ts

+13-4
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,30 @@ export const loggerMiddleware =
1616
context: HandlerExecutionContext
1717
): InitializeHandler<any, Output> =>
1818
async (args: InitializeHandlerArguments<any>): Promise<InitializeHandlerOutput<Output>> => {
19-
const { clientName, commandName, inputFilterSensitiveLog, logger, outputFilterSensitiveLog } = context;
20-
2119
const response = await next(args);
20+
const {
21+
clientName,
22+
commandName,
23+
logger,
24+
inputFilterSensitiveLog,
25+
outputFilterSensitiveLog,
26+
dynamoDbDocumentClientOptions = {},
27+
} = context;
28+
29+
const { overrideInputFilterSensitiveLog, overrideOutputFilterSensitiveLog } = dynamoDbDocumentClientOptions;
2230

2331
if (!logger) {
2432
return response;
2533
}
2634

2735
if (typeof logger.info === "function") {
2836
const { $metadata, ...outputWithoutMetadata } = response.output;
37+
2938
logger.info({
3039
clientName,
3140
commandName,
32-
input: inputFilterSensitiveLog(args.input),
33-
output: outputFilterSensitiveLog(outputWithoutMetadata),
41+
input: (overrideInputFilterSensitiveLog ?? inputFilterSensitiveLog)(args.input),
42+
output: (overrideOutputFilterSensitiveLog ?? outputFilterSensitiveLog)(outputWithoutMetadata),
3443
metadata: $metadata,
3544
});
3645
}

Diff for: packages/types/src/middleware.ts

+8
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,14 @@ export interface HandlerExecutionContext {
407407
*/
408408
authSchemes?: AuthScheme[];
409409

410+
/**
411+
* Used by DynamoDbDocumentClient.
412+
*/
413+
dynamoDbDocumentClientOptions?: Partial<{
414+
overrideInputFilterSensitiveLog(...args: any[]): string | void;
415+
overrideOutputFilterSensitiveLog(...args: any[]): string | void;
416+
}>;
417+
410418
[key: string]: any;
411419
}
412420

0 commit comments

Comments
 (0)