NERDVANA has asked for the wisdom of the Perl Monks concerning the following question:

I have a module Crypt::SecretBuffer which prevents a scalar from being seen "unless you really want to" (ideally passing it directly to an XS function to prevent copies from being made). Right now the API for viewing the secret is using 'local' on an attribute 'stringify_mask' and an overloaded stringification on the object:
$password= secret(...); ... local $password->{stringify_mask}= undef if ref($password) eq 'Crypt:: +SecretBuffer'; $db= DBI->connect($dsn, $user, $password, \%attr);

On Github, robrwo [suggested a new more functional-programming-like API, namely that a method of SecretBuffer would pass the secret to a callback. My thought on the matter was that the *real* missing feature is a duck-typing API that doesn't need to be specific to SecretBuffer, which should provide an easy and convenient recipe for code that doesn't have a hard dependency on SecretBuffer to gain access to secrets.

I'd like to conduct an informal poll of which design people like the best:

  1. "apply", a method which takes a callback of one argument:
    if ($password isa 'Crypt::SecretBuffer') { $db= $password->apply(sub { DBI->connect($dsn, $user, $_[0], \%attr) + }); else { $db= DBI->connect($dsn, $user, $password, \%attr); }
  2. A more distinct method name that could be used for duck-typing and apply to other types of object:
    if (blessed $password && $password->can("unmask_secret_to")) { $db= $password->unmask_secret_to(\&DBI::connect, 'DBI', $dsn, $user, + $password, \%attr); } else { $db= DBI->connect($dsn, $user, $password, \%attr); }
    1. reveal_secret_to
    2. unmask_secret_to
    3. call_with_secret
    4. call_unmasked
  3. Use a scalar-ref overload:
    $db= DBI->connect($dsn, $user, ref $password? $$password : $password, +\%attr);

I just came up with that scalar-ref idea, and seems pretty safe because no code would normally scalar-dereference a ref unless the ref type was 'SCALAR'. Still, that's drastically lowering the bar for leaking the secret, and isn't advertising that it has this capability...

While pondering your preference, also consider the question "If someone contributed a patch to a module I maintain that added support for receiving secrets via SecretBuffer, how much boilerplate would I be willing to accept?"

Replies are listed 'Best First'.
Re: Name a method that exposes a secret
by duelafn (Parson) on Sep 30, 2025 at 19:32 UTC

    I see two oddities in the proposed unmask_secret_to. First, you have to use the buffer both as the invocant as well as an argument which is odd. Second, If you had two secrets that interface would be confusing, does it unmask the other secret or not?

    my $other_secret = Crypt::SecretBuffer->new("Foo"); $password->unmask_secret_to(\&do_something, $user, $password, $other_s +ecret);

    Making unmask_secrets_to a plain exportable sub, not a method would address these issues (at least makes it clear what will happen in the second case). You could also implement an unmask_to (apply) method which would allow fine-grained access for multiple secret cases (or some people will just prefer that approach). So, "why not both"?

    I do not like #3, it is too easy to do accidentally and entirely un-greppable.

    On bikeshedding, for something that unmasks a secret, a generic name like "apply" isn't a good match. I'd suggest something greppable like unmask_to (which would be consistent with the unmask_secrets_to sub), or call_with_unmasked, or just unmask,

    my $other_secret = Crypt::SecretBuffer->new("Foo"); $password->unmask_to(sub($pass) { do_something($user, $pass, $other_se +cret) });

    Update: Supporting SecretBuffers in another library could, I guess, look a bit like this (in the typical case of just wanting to unwrap any secrets passed in):

    use 5.036; *unmask_secrets_to = eval { require Crypt::SecretBuffer; \&Crypt::SecretBuffer::unmask_ +secrets_to } || sub($cb, @args) { &$cb(@args) } ; sub private_fn { say "@_"; } sub my_api_function($user, $password, $secret) { unmask_secrets_to(\&private_fn, $user, $password, $secret); }

    Good Day,
        Dean