mirror of
https://github.com/Luzifer/vault2env.git
synced 2024-12-20 12:11:19 +00:00
113 lines
2.8 KiB
Go
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]))
|
||
|
}
|
||
|
}
|