github.com/anchore/syft@v1.38.2/syft/format/internal/testutil/redactor.go (about) 1 package testutil 2 3 import ( 4 "bytes" 5 "regexp" 6 ) 7 8 var ( 9 _ Redactor = (*RedactorFn)(nil) 10 _ Redactor = (*PatternReplacement)(nil) 11 _ Redactor = (*ValueReplacement)(nil) 12 _ Redactor = (*Redactions)(nil) 13 ) 14 15 type Redactor interface { 16 Redact([]byte) []byte 17 } 18 19 // Replace by function ////////////////////////////// 20 21 type RedactorFn func([]byte) []byte 22 23 func (r RedactorFn) Redact(b []byte) []byte { 24 return r(b) 25 } 26 27 // Replace by regex ////////////////////////////// 28 29 type PatternReplacement struct { 30 Search *regexp.Regexp 31 Groups []string 32 Replace string 33 } 34 35 func NewPatternReplacement(r *regexp.Regexp) PatternReplacement { 36 return PatternReplacement{ 37 Search: r, 38 Replace: "redacted", 39 } 40 } 41 42 func (p PatternReplacement) Redact(b []byte) []byte { 43 if len(p.Groups) == 0 { 44 return p.Search.ReplaceAll(b, []byte(p.Replace)) 45 } 46 47 return p.redactNamedGroups(b) 48 } 49 50 func (p PatternReplacement) redactNamedGroups(b []byte) []byte { 51 groupsToReplace := make(map[string]bool) 52 for _, g := range p.Groups { 53 groupsToReplace[g] = true 54 } 55 56 subexpNames := p.Search.SubexpNames() 57 58 return p.Search.ReplaceAllFunc(b, func(match []byte) []byte { 59 indexes := p.Search.FindSubmatchIndex(match) 60 if indexes == nil { 61 return match 62 } 63 64 result := make([]byte, len(match)) 65 copy(result, match) 66 67 // keep track of the offset as we replace groups 68 offset := 0 69 70 // process each named group 71 for i, name := range subexpNames { 72 // skip the full match (i==0) and groups we don't want to replace 73 if i == 0 || !groupsToReplace[name] { 74 continue 75 } 76 77 // get the start and end positions of this group 78 startPos := indexes[2*i] 79 endPos := indexes[2*i+1] 80 81 // skip if the group didn't match 82 if startPos < 0 || endPos < 0 { 83 continue 84 } 85 86 // adjust positions based on previous replacements 87 startPos += offset 88 endPos += offset 89 90 // replace the group with our replacement text 91 beforeGroup := result[:startPos] 92 afterGroup := result[endPos:] 93 94 // calculate the new offset 95 oldLen := endPos - startPos 96 newLen := len(p.Replace) 97 offset += (newLen - oldLen) 98 99 result = append(beforeGroup, append([]byte(p.Replace), afterGroup...)...) //nolint:gocritic 100 } 101 102 return result 103 }) 104 } 105 106 // Replace by value ////////////////////////////// 107 108 type ValueReplacement struct { 109 Search string 110 Replace string 111 } 112 113 func NewValueReplacement(v string) ValueReplacement { 114 return ValueReplacement{ 115 Search: v, 116 Replace: "redacted", 117 } 118 } 119 120 func (v ValueReplacement) Redact(b []byte) []byte { 121 return bytes.ReplaceAll(b, []byte(v.Search), []byte(v.Replace)) 122 } 123 124 // Handle a collection of redactors ////////////////////////////// 125 126 type Redactions struct { 127 redactors []Redactor 128 } 129 130 func NewRedactions(redactors ...Redactor) *Redactions { 131 r := &Redactions{ 132 redactors: redactors, 133 } 134 135 return r.WithFunctions(carriageRedactor) 136 } 137 138 func (r *Redactions) WithPatternRedactors(values map[string]string) *Redactions { 139 for k, v := range values { 140 r.redactors = append(r.redactors, 141 PatternReplacement{ 142 Search: regexp.MustCompile(k), 143 Replace: v, 144 }, 145 ) 146 } 147 return r 148 } 149 150 func (r *Redactions) WithPatternRedactorSpec(values ...PatternReplacement) *Redactions { 151 for _, v := range values { 152 r.redactors = append(r.redactors, v) 153 } 154 return r 155 } 156 157 func (r *Redactions) WithValueRedactors(values map[string]string) *Redactions { 158 for k, v := range values { 159 r.redactors = append(r.redactors, 160 ValueReplacement{ 161 Search: k, 162 Replace: v, 163 }, 164 ) 165 } 166 return r 167 } 168 169 func (r *Redactions) WithPatternsRedacted(values ...string) *Redactions { 170 for _, pattern := range values { 171 r.redactors = append(r.redactors, 172 NewPatternReplacement(regexp.MustCompile(pattern)), 173 ) 174 } 175 return r 176 } 177 178 func (r *Redactions) WithValuesRedacted(values ...string) *Redactions { 179 for _, v := range values { 180 r.redactors = append(r.redactors, 181 NewValueReplacement(v), 182 ) 183 } 184 return r 185 } 186 187 func (r *Redactions) WithFunctions(values ...func([]byte) []byte) *Redactions { 188 for _, fn := range values { 189 r.redactors = append(r.redactors, 190 RedactorFn(fn), 191 ) 192 } 193 return r 194 } 195 196 func (r *Redactions) WithRedactors(rs ...Redactor) *Redactions { 197 r.redactors = append(r.redactors, rs...) 198 return r 199 } 200 201 func (r Redactions) Redact(b []byte) []byte { 202 for _, redactor := range r.redactors { 203 b = redactor.Redact(b) 204 } 205 return b 206 } 207 208 func carriageRedactor(s []byte) []byte { 209 return bytes.ReplaceAll(s, []byte("\r\n"), []byte("\n")) 210 }