9

Preamble

I often share my screen with other developers over discord. When I navigate to a .env file that has sensitive information, I'd like to conceal that information while still allowing others to see the context.

Example file:

DATABASE_URL="postgresql://dummy:[email protected]:5432/dummy"
HEDERA_TOPIC_ID=0.0.1624219
INFURA_API_KEY=somesecretkeyidrathernotshare
KMS_SECRET_KEY=
WALLET_PRIVATE_KEY=

Desired result:

DATABASE_URL="postgresql://dummy:[email protected]:5432/dummy"
HEDERA_TOPIC_ID=0.0.1624219
INFURA_API_KEY=*****************************
KMS_SECRET_KEY=
WALLET_PRIVATE_KEY=

I first searched for a plugin that does exactly this, but I couldn't find anything suitable.

Attempts

First Attempt

:syn match Secrets /^.*KEY=.*$/ conceal cchar=*
:setl cole=2 cocu=nvc

Which looks like this:

*

All context is lost.

Second Attempt

:syn match Secrets /\(^.*KEY=\)\@<=.*$/ conceal cchar=*
:setl cole=2 cocu=nvc

Which produces:

INFURA_API_KEY=*

Better, but it's not that easy to see and doesn't look nice.

1
  • 5
    Frame challenge: You should not store passwords or other sensitive information in config files. You should use a key management infrastructure that provides passwords during runtime.
    – Marcel
    Commented Mar 9 at 23:20

2 Answers 2

11

I would recommend to use matchadd() function for this, because it is independent of syntax highlighting and a bit more flexible.

For your particular use case, the following should work:

call matchadd('Conceal', '\(KEY=.*\)\@<=.', 10, -1, {'conceal': '*'})

The key here is to use \@<= regex atom that matches for each character after the KEY=. followed by any character, so it can replace each password character by a simple *.

In addition you need to have the following settings, to make sure concealing works as you expect:

:set conceallevel=2 concealcursor=nv

This will make sure to use the conceal character instead of highlighting simply the match and will keep it concealed in normal and visual mode, even if the cursor is on the line which contains your password.

See the documentation for those options here: 'conceallevel', 'concealcursor'

Related answer from a few years ago: https://stackoverflow.com/a/34544986/789222

2
  • I find this doesn't work if the screen is split. The current active window takes the conceal, leaving the original unprotected. Is there a way for this to apply to every window at the same time? Commented Mar 11 at 0:44
  • 1
    matches are window-local. So you need to combine this with a WinEnter/BufWinEnter autocommand Commented Mar 11 at 7:19
5

Working solution 1

:syn match SecretKeys /\(^.*_KEY=\)\@<=.*$/ contains=SecretKeyChars
:syn match SecretKeyChars /.\ze\_./ conceal contained cchar=*
:setl cole=2 cocu=nvc

The SecretKeys group defines the context of the match, i.e. the region where the other group SecretKeyChars will operate.

SecretKeyChars matches anything, but one at a time \ze. The final \_. means that the singular match against anything will work if the next character is "anything" or an eol (without this the last character is still visible).

cole is conceallevel and 2 allows the cchar to be visible. cocu is concealcursor and defines the modes when the conceal will be active, in this case nvc: normal, visual, and command line editing. This means that the password will be visible if I try to edit the line.

Lua config:

-- Conceal paswords and secrets
local conceal_secrets_group = vim.api.nvim_create_augroup("ConcealSecretsGroup", {})
vim.api.nvim_create_autocmd({ "BufEnter" }, {
    group = conceal_secrets_group,
    callback = function()
        vim.cmd.syntax([[match SecretKeyChars /.\ze\_./ conceal contained cchar=*]])
        vim.cmd.syntax([[match SecretKeys /\(^.*_KEY=\)\@<=.*$/ contains=SecretKeyChars]])
    end,
})
vim.opt.conceallevel = 2
vim.opt.concealcursor = "nvc"

This triggers everytime a buffer is entered. To extend, copy-paste the vim.cmd.syntax lines, change the match names (SecretKeys, SecretKeyChars) and match pattern (^.*_KEY=)

Working Solution 2

Uses matchadd solution described by @christian

Lua config:

-- Conceal paswords and secrets
local conceal_secrets_group = vim.api.nvim_create_augroup("ConcealSecretsGroup", {})
vim.api.nvim_create_autocmd({ "WinEnter", "BufWinEnter" }, {
    group = conceal_secrets_group,
    callback = function()
        vim.fn.matchadd('Conceal', '\\(KEY=.*\\)\\@<=.', 10, -1, {conceal = '*'})
    end,
})
vim.opt.conceallevel = 2
vim.opt.concealcursor = "nv"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.