Skip to content

Commit

Permalink
feat: Redact or remove paths
Browse files Browse the repository at this point in the history
Pino has a limitation where you can either redact or remove specified
`redact.paths` by setting `redact.remove` to `true` or `false`.

A logger option has been added to allow you to redact some paths and
remove other paths.

Example:

```typescript
const logger = createLogger({
  name: 'my-app',
  redact: {
    paths: ['req.headers["x-redact-this"]'],
    removePaths: ['req.headers["x-remove-this"]'],
  },
});
```
  • Loading branch information
ConradLang committed Aug 29, 2023
1 parent b40bfed commit a9f4d13
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 11 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,26 @@ logger.error<Fields>(

Bearer tokens are redacted regardless of their placement in the log object.

#### Pino Redaction

To redact other properties, use the `redact` logger options as per [pino redaction] docs.

Pino has a limitation where you can either redact or remove specified `redact.paths` by setting `redact.remove` to `true` or `false`.

A logger option has been added to allow you to redact some paths and remove other paths.

Example:

```typescript
const logger = createLogger({
name: 'my-app',
redact: {
paths: ['req.headers["x-redact-this"]'],
removePaths: ['req.headers["x-remove-this"]'],
},
});
```

### Trimming

The following trimming rules apply to all logging data:
Expand Down Expand Up @@ -162,3 +182,4 @@ const logger = createLogger({
[pino]: https://github.com/pinojs/pino
[pino options]: https://github.com/pinojs/pino/blob/master/docs/api.md#options
[pino-pretty]: https://github.com/pinojs/pino-pretty
[pino redaction]: https://github.com/pinojs/pino/blob/master/docs/api.md#redact-array--object
48 changes: 48 additions & 0 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ function testLog(
output: any,
method?: 'error' | 'info',
loggerOptions?: LoggerOptions,
shouldNotHavePropertyPaths?: string[],
) {
// eslint-disable-next-line jest/valid-title
test(testName, async () => {
Expand All @@ -50,6 +51,9 @@ function testLog(
expect(log).toMatchObject(output);
expect(inputString).toEqual(JSON.stringify(input));
expect(log).toHaveProperty('timestamp');
shouldNotHavePropertyPaths?.forEach((path) => {
expect(log).not.toHaveProperty(path);
});
});
}

Expand Down Expand Up @@ -422,6 +426,50 @@ testLog(
},
);

testLog(
'should redact or remove specified paths',
{
msg: 'allowed',
req: {
headers: {
['x-remove-me']: 'Should be removed',
secret: 'Should be redacted',
},
},
},
{ msg: 'allowed', req: { headers: { secret: '[Redacted]' } } },
'info',
{
redact: {
paths: ['req.headers.secret'],
removePaths: ['req.headers["x-remove-me"]'],
},
},
['req.headers.x-remove-me'],
);

testLog(
'should remove specified paths',
{
msg: 'allowed',
req: {
headers: {
['x-remove-me']: 'Should be removed',
secret: 'Should be removed',
},
},
},
{ msg: 'allowed', req: { headers: {} } },
'info',
{
redact: {
paths: [],
removePaths: ['req.headers.secret', 'req.headers["x-remove-me"]'],
},
},
['req.headers.secret', 'req.headers.x-remove-me'],
);

interface ExampleMessageContext {
activity: string;
err?: {
Expand Down
7 changes: 6 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import serializers from './serializers';

export { pino };

export type LoggerOptions = pino.LoggerOptions & FormatterOptions;
export type LoggerOptions = Omit<pino.LoggerOptions, 'redact'> &
FormatterOptions & { redact?: redact.ExtendedRedactOptions };
export type Logger = pino.Logger;

/**
Expand All @@ -25,6 +26,10 @@ export default (
}),
): Logger => {
opts.redact = redact.addDefaultRedactPathStrings(opts.redact);
opts.redact = redact.addRemovePathsCensor(
opts.redact,
opts.redact?.removePaths,
);
opts.serializers = {
...serializers,
...opts.serializers,
Expand Down
43 changes: 33 additions & 10 deletions src/redact/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import type * as pino from 'pino';

// TODO: Redact cookies?
export const defaultRedact = [];

/**
* Private interface vendored from `pino`
*/
interface redactOptions {
paths: string[];
censor?: string | ((value: unknown, path: string[]) => unknown);
remove?: boolean;
}
export type ExtendedRedactOptions = pino.LoggerOptions['redact'] & {
/**
* A list of paths to remove from the logged object instead of redacting them.
* Note that if you are only removing, rather use the `paths` property and set `remove` to `true`.
*/
removePaths?: string[];
};

type ExtendedRedact = string[] | ExtendedRedactOptions | undefined;

export const addDefaultRedactPathStrings = (
redact: string[] | redactOptions | undefined,
) => {
redact: ExtendedRedact,
): ExtendedRedact => {
if (!redact) {
return defaultRedact;
}
Expand All @@ -21,3 +24,23 @@ export const addDefaultRedactPathStrings = (
}
return redact;
};

export const addRemovePathsCensor = (
redact: ExtendedRedact,
removePaths?: string[],
): ExtendedRedact => {
if (!removePaths || removePaths.length === 0) {
return redact;
}

const redactPaths =
redact && Array.isArray(redact) ? redact : redact?.paths ?? defaultRedact;

return redactPaths.length === 0
? { paths: removePaths, remove: true }
: {
paths: [...redactPaths, ...removePaths],
censor: (_value, path) =>
redactPaths.includes(path.join('.')) ? '[Redacted]' : undefined,
};
};

0 comments on commit a9f4d13

Please sign in to comment.