Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Allow some paths to be redacted and some to be removed #84

Closed
wants to merge 8 commits into from

Conversation

ConradLang
Copy link
Contributor

@ConradLang ConradLang commented Aug 29, 2023

Purpose

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

In some cases, it's reasonable to want to redact some properties e.g. headers.authorization so you know that you have an authorization header but in other cases, the header, e.g. x-envoy-peer-metadata only contributes to increasing the log message size and therefore cost.

To reduce costs, we should be able to remove the properties that are not relevant for diagnostic purposes.

Approach

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

Example:

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

Default paths can be added for removal if needed (currently an empty list):

export const defaultRemovePaths: string[] = [];

If you'd prefer not to remove the default paths, you can opt out by setting ignoreDefaultRemovePaths to true.

Example:

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

Notes

  1. Reverted Vendor redactOptions #42 to allow redactOptions to be extended.
    Running yarn build succeeds without errors.
  2. Added an expectation in the test for properties not to be present.

Issues

Resolves #83

@ConradLang ConradLang requested a review from 72636c as a code owner August 29, 2023 00:50
@ConradLang ConradLang self-assigned this Aug 29, 2023
@ConradLang ConradLang marked this pull request as draft August 29, 2023 00:50
@ConradLang ConradLang marked this pull request as ready for review August 29, 2023 01:29
@ConradLang ConradLang changed the title feat: Redact or remove paths feat: Allow some paths to be redacted and some to be removed Aug 29, 2023
src/index.ts Outdated
Comment on lines 28 to 29
opts.redact = redact.addDefaultRedactPathStrings(opts.redact);
opts.redact = redact.addRemovePathsCensor(opts.redact);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would a better name be configureCensor?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit sceptical of this chaining of two standalone redaction functions. Is it obvious how they both modify opts.redact, and what guarantees that they evolve in a compatible, non-conflicting manner?

At minimum we will want tests that cover combinations of both, especially if we ponder adding default redaction config.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've exported a configureRedact function that ensures the default redact paths are added before the censor is configured.

name: 'my-app',
redact: {
paths: ['req.headers["x-redact-this"]'],
removePaths: ['req.headers["x-remove-this"]'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there sensible defaults that we should be thinking of bundling into this package, rather than expecting each team to manually review and add removePaths?

Copy link
Contributor Author

@ConradLang ConradLang Aug 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some x-envoy-* headers likely related to internal routing but I'm not sure if these should be blanket added for all services.

Also, being OSS, should we add default headers for removal or is it ok since it's a SEEK opinionated logger?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's SEEK opinionated so I'd be fine with any configuration that is not sensitive, and I reckon headers for an OSS project like Envoy would be fine.

Do we have an example of a SEEK application that would be impaired by having x-envoy-* removed by default? We'd presumably allow them to override/disable the defaults if needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've asked cloud platform about the x-envoy-* headers so we'll have to wait for an answer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah apologies if I was vague here; I meant adding logic here to remove the headers from logs by default, not changing our infra to stop attaching the headers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@72636c : I've just pushed a change that allows us to add default paths for removal.

Caveat: Since the defaultRemovePaths is empty, it isn't really tested, but you can pull this branch, add a path e.g. req.headers["x-envoy-peer-metadata"] and see the should remove default paths when ignoreDefaultRemovePaths is missing test result.
You might need to console.log({input, log}) in the testLog function to observe.

You can also ignoreDefaultRemovePaths if you don't want the default remove paths to be added.

I had to refactor the addDefaultRedactPathStrings (and rename to appendDefaultRedactAndRemove) to get this working.

Something I'm wondering is should we only override the censor when it is undefined with the caveat that the remove may not work proper?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something I'm wondering is should we only override the censor when it is undefined with the caveat that the remove may not work proper?

I've updated this so that the input censor is used for redaction but removePaths properties are still removed.

src/redact/index.ts Outdated Show resolved Hide resolved
Comment on lines 51 to 52
censor: (_value, path) =>
redactMap[path.join('.')] ? '[Redacted]' : undefined,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a bit dicey. How does the redaction package handle the difference between e.g.

{ a: { b: 'pii' } }

{ 'a.b': 'pii' }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've resolved this issue by refactoring out the key builder and putting in a comprehensive test.

I've used a regular expression as I'm not sure of a better way to work out if the identifier name is out-of-spec.

@@ -21,3 +24,46 @@ export const addDefaultRedactPathStrings = (
}
return redact;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@72636c , @etaoins : Seems like the default redact paths aren't added when redact is an object.

I feel like this may be an issue separate to this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"]'],
  },
});
```
Property access should be more performant than string compare.
Renamed to configureRedactCensor.
Default redact paths should always be added before configuring the
censor.
Also corrected `ExtendedRedact` definition as `removePaths` should only
have been on the pino `redactOptions` type.
@kosanna
Copy link
Collaborator

kosanna commented Sep 26, 2023

Will convert to draft while discussions are in progress

@ConradLang
Copy link
Contributor Author

Closing this PR as #92 was more appropriate and the concensus from the upstream repo (pinojs/pino#1815) was that a custom serializer was the best option.

To address #83, we could consider adding guidance to the readme around usage of redact and the option to use a custom serializer to remove specific properties with some examples.

@ConradLang ConradLang closed this Oct 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Logger can only redact or remove properties, not both
4 participants