106 lines
2.7 KiB
Go
106 lines
2.7 KiB
Go
package codec
|
|
|
|
import (
|
|
"bytes"
|
|
|
|
"github.com/zricethezav/gitleaks/v8/logging"
|
|
)
|
|
|
|
// Decoder decodes various types of data in place
|
|
type Decoder struct {
|
|
decodedMap map[string]string
|
|
}
|
|
|
|
// NewDecoder creates a default decoder struct
|
|
func NewDecoder() *Decoder {
|
|
return &Decoder{
|
|
decodedMap: make(map[string]string),
|
|
}
|
|
}
|
|
|
|
// Decode returns the data with the values decoded in place along with the
|
|
// encoded segment meta data for the next pass of decoding
|
|
func (d *Decoder) Decode(data string, predecessors []*EncodedSegment) (string, []*EncodedSegment) {
|
|
segments := d.findEncodedSegments(data, predecessors)
|
|
|
|
if len(segments) > 0 {
|
|
result := bytes.NewBuffer(make([]byte, 0, len(data)))
|
|
encodedStart := 0
|
|
for _, segment := range segments {
|
|
result.WriteString(data[encodedStart:segment.encoded.start])
|
|
result.WriteString(segment.decodedValue)
|
|
encodedStart = segment.encoded.end
|
|
}
|
|
|
|
result.WriteString(data[encodedStart:])
|
|
return result.String(), segments
|
|
}
|
|
|
|
return data, segments
|
|
}
|
|
|
|
// findEncodedSegments finds the encoded segments in the data
|
|
func (d *Decoder) findEncodedSegments(data string, predecessors []*EncodedSegment) []*EncodedSegment {
|
|
if len(data) == 0 {
|
|
return []*EncodedSegment{}
|
|
}
|
|
|
|
decodedShift := 0
|
|
encodingMatches := findEncodingMatches(data)
|
|
segments := make([]*EncodedSegment, 0, len(encodingMatches))
|
|
for _, m := range encodingMatches {
|
|
encodedValue := data[m.start:m.end]
|
|
decodedValue, alreadyDecoded := d.decodedMap[encodedValue]
|
|
|
|
if !alreadyDecoded {
|
|
decodedValue = m.encoding.decode(encodedValue)
|
|
d.decodedMap[encodedValue] = decodedValue
|
|
}
|
|
|
|
if len(decodedValue) == 0 {
|
|
continue
|
|
}
|
|
|
|
segment := &EncodedSegment{
|
|
predecessors: predecessors,
|
|
original: toOriginal(predecessors, m.startEnd),
|
|
encoded: m.startEnd,
|
|
decoded: startEnd{
|
|
m.start + decodedShift,
|
|
m.start + decodedShift + len(decodedValue),
|
|
},
|
|
decodedValue: decodedValue,
|
|
encodings: m.encoding.kind,
|
|
depth: 1,
|
|
}
|
|
|
|
// Shift decoded start and ends based on size changes
|
|
decodedShift += len(decodedValue) - len(encodedValue)
|
|
|
|
// Adjust depth and encoding if applicable
|
|
if len(segment.predecessors) != 0 {
|
|
// Set the depth based on the predecessors' depth in the previous pass
|
|
segment.depth = 1 + segment.predecessors[0].depth
|
|
// Adjust encodings
|
|
for _, p := range segment.predecessors {
|
|
if segment.encoded.overlaps(p.decoded) {
|
|
segment.encodings |= p.encodings
|
|
}
|
|
}
|
|
}
|
|
|
|
segments = append(segments, segment)
|
|
logging.Debug().
|
|
Str("decoder", m.encoding.kind.String()).
|
|
Msgf(
|
|
"segment found: original=%s pos=%s: %q -> %q",
|
|
segment.original,
|
|
segment.encoded,
|
|
encodedValue,
|
|
segment.decodedValue,
|
|
)
|
|
}
|
|
|
|
return segments
|
|
}
|