1
0
mirror of https://github.com/Luzifer/vault2env.git synced 2024-09-16 15:48:33 +00:00
vault2env/obfuscator_writer.go
Knut Ahlers 2fb748b87a
Rewrite obfuscator logic not to work line-based
Signed-off-by: Knut Ahlers <knut@ahlers.me>
2023-12-01 00:32:07 +01:00

113 lines
2.8 KiB
Go

package main
import (
"bytes"
"fmt"
"io"
"sort"
)
type (
obfuscator struct {
buffer []byte
closed bool
longestSecretLen int
output io.Writer
secretReplacements [][2]string
}
replaceFn func(name, secret string) string
)
var _ io.WriteCloser = &obfuscator{}
func newObfuscator(output io.Writer, secrets map[string]string, fn replaceFn) *obfuscator {
// We're looking for the longest secret: That is half the amount of
// data we need to keep in the buffer in order to detect and replace
// the secrets before forwarding the data to the real writer
var longestSecretLen int
for _, s := range secrets {
if l := len(s); l > longestSecretLen {
longestSecretLen = l
}
}
var replacements [][2]string
if fn == nil {
// Special case: No replacer is set, we can pass-through
longestSecretLen = 0
} else {
// Create a map of replacements
for name, secret := range secrets {
replacements = append(replacements, [2]string{secret, fn(name, secret)})
}
}
sort.Slice(replacements, func(j, i int) bool {
return len(replacements[i][0]) < len(replacements[j][0])
})
return &obfuscator{
longestSecretLen: longestSecretLen,
output: output,
secretReplacements: replacements,
}
}
func (o *obfuscator) Close() (err error) {
o.closed = true
// Do a last sweep on the remaining buffer
o.sanitizeBuffer()
// Copy the rest to the underlying writer
if _, err = o.output.Write(o.buffer); err != nil {
return fmt.Errorf("writing remaining buffer: %w", err)
}
return nil
}
func (o *obfuscator) Write(data []byte) (n int, err error) {
if o.closed {
return 0, fmt.Errorf("write on closed writer")
}
// First take everything from the input
o.buffer = append(o.buffer, data...)
// If we haven't enough data buffered lets just pretent we wrote
// everything and in reality do nothing
if len(o.buffer) < o.longestSecretLen*2 {
return len(data), nil
}
// Now we have at least twice the length of the longest secret in
// the buffer so we can sanitize the buffer…
o.sanitizeBuffer()
// Now that all secrets have been replaced, we can write everything
// to the writer except the last {longestSecretLen} bytes as they
// might contain a part of the longest secret
wrLen := len(o.buffer) - o.longestSecretLen
if wrLen < 1 {
// Nothing to write, buffer was shortened too much
return len(data), nil
}
if _, err = io.Copy(o.output, bytes.NewReader(o.buffer[:wrLen])); err != nil {
return 0, fmt.Errorf("copying sanitized data to writer: %w", err)
}
o.buffer = o.buffer[wrLen:]
// We took everything from them: Lets tell them we wrote everything
return len(data), nil
}
func (o *obfuscator) sanitizeBuffer() {
for _, repl := range o.secretReplacements {
o.buffer = bytes.ReplaceAll(o.buffer, []byte(repl[0]), []byte(repl[1]))
}
}