Skip to content

Protect secrets from __repr__ #471

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

Closed
webknjaz opened this issue Dec 10, 2018 · 6 comments
Closed

Protect secrets from __repr__ #471

webknjaz opened this issue Dec 10, 2018 · 6 comments
Labels

Comments

@webknjaz
Copy link
Member

Looking at https://github.com/hynek/environ_config and my internal attrs classes representing a token response from GitHub API etc.

I know about repr=False (and I saw this hack) but in my case I don't want to "hide" the attr existence. I want to be able to sanitize it's value.
At @ansible we have this feature where we can mark vars at different levels as no_log and they appeared as censored. I assume it's a quite widely used practice.

With that in mind I'm currently thinking of these few ways:

  1. full protection
>>> repr(AttrsClass(secret='value'))
AttrsClass(secret=attr.CENSORED_VAL)  # hide everything
  1. partially sanitized
>>> repr(AttrsClass(secret='value'))
AttrsClass(secret=attr.CENSORED_VAL('va...e'))  # show some helper parts usually sufficient to recognize that token is smth odd when debugging things
  1. could be also useful to preserve a pattern
>>> repr(AttrsClass(secret='DFJ-3E3-DF83'))
AttrsClass(secret=attr.CENSORED_VAL('XXX-XXX-XXXX'))

P.S. I saw #212 and #453 but I believe this use-case should get a first-class support separately from those.

@wsanchez
Copy link

My feeling is that this feature is too specific to be in attrs proper, especially the partially sanitized and pattern aspects.

@RonnyPfannschmidt
Copy link

@webknjaz wouldn't this better be served by having a very specific string class and using it to encode secrets? then this would just be a converter as far as attrs is concerned

@webknjaz
Copy link
Member Author

webknjaz added a commit to sanitizers/chronographer-github-app that referenced this issue Dec 10, 2018
@droserasprout
Copy link

A bunch of dirty hacks to mask secret values conditionally at the runtime:

from inspect import isclass

from attr import dataclass
from attr._make import _add_repr
from typing import NewType, Optional


class Secret:
    def __new__(cls, typ):
        return NewType('Secret', typ)(cls)


@dataclass(kw_only=True)
class SecretClass:
    top_secret: Secret(Optional[str])

    def mask_secrets(self):
        for attrib in self.__class__.__attrs_attrs__:
            if isclass(attrib.type) and issubclass(attrib.type, Secret):
                object.__setattr__(attrib, 'repr', lambda _: '[MASKED]')
        self.__class__ = _add_repr(self.__class__)

@hynek
Copy link
Member

hynek commented Oct 31, 2019

You can pass a callable for repr since 19.2 – should't that solve your problem?

@webknjaz
Copy link
Member Author

I guess. It may take some time to actually try it out, though. I'll close this issue for now and will get back or create a new one if something won't satisfy my expectations. Thanks :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants