mirror of
https://github.com/kubescape/kubescape.git
synced 2026-02-14 09:59:54 +00:00
fix: follow newline conventions of the autofixed file
This change makes the autofix handler use the newline separator defined in the fixed file for writing its changes.
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
package fixhandler
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
metav1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
|
||||
"github.com/kubescape/opa-utils/reporthandling"
|
||||
@@ -43,7 +46,7 @@ type fixInfoMetadata struct {
|
||||
linesToRemove *[]linesToRemove
|
||||
}
|
||||
|
||||
// ContentToAdd holds the information about where to insert the new changes in the existing yaml file
|
||||
// contentToAdd holds the information about where to insert the new changes in the existing yaml file
|
||||
type contentToAdd struct {
|
||||
// Line where the fix should be applied to
|
||||
line int
|
||||
@@ -51,6 +54,49 @@ type contentToAdd struct {
|
||||
content string
|
||||
}
|
||||
|
||||
func withNewline(content, targetNewline string) string {
|
||||
replaceNewlines := map[string]bool{
|
||||
unixNewline: true,
|
||||
windowsNewline: true,
|
||||
oldMacNewline: true,
|
||||
}
|
||||
replaceNewlines[targetNewline] = false
|
||||
|
||||
newlinesToReplace := make([]string, len(replaceNewlines))
|
||||
i := 0
|
||||
for k := range replaceNewlines {
|
||||
newlinesToReplace[i] = k
|
||||
i++
|
||||
}
|
||||
|
||||
// To ensure that we fully replace Windows newlines (CR LF), and not
|
||||
// corrupt them into two new newlines (CR CR or LF LF) by partially
|
||||
// replacing either CR or LF, we have to ensure we replace longer
|
||||
// Windows newlines first
|
||||
sort.Slice(newlinesToReplace, func(i int, j int) bool {
|
||||
return len(newlinesToReplace[i]) > len(newlinesToReplace[j])
|
||||
})
|
||||
|
||||
// strings.Replacer takes a flat list of (oldVal, newVal) pairs, so we
|
||||
// need to allocate twice the space and assign accordingly
|
||||
newlinesOldNew := make([]string, 2*len(replaceNewlines))
|
||||
i = 0
|
||||
for _, nl := range newlinesToReplace {
|
||||
newlinesOldNew[2*i] = nl
|
||||
newlinesOldNew[2*i+1] = targetNewline
|
||||
i++
|
||||
}
|
||||
|
||||
replacer := strings.NewReplacer(newlinesOldNew...)
|
||||
return replacer.Replace(content)
|
||||
}
|
||||
|
||||
// Content returns the content that will be added, separated by the explicitly
|
||||
// provided `targetNewline`
|
||||
func (c *contentToAdd) Content(targetNewline string) string {
|
||||
return withNewline(c.content, targetNewline)
|
||||
}
|
||||
|
||||
// LinesToRemove holds the line numbers to remove from the existing yaml file
|
||||
type linesToRemove struct {
|
||||
startLine int
|
||||
|
||||
89
core/pkg/fixhandler/datastructures_test.go
Normal file
89
core/pkg/fixhandler/datastructures_test.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package fixhandler
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestContentNewlinesMatchTarget(t *testing.T) {
|
||||
cases := []struct {
|
||||
Name string
|
||||
InputContent string
|
||||
TargetNewline string
|
||||
WantedContent string
|
||||
}{
|
||||
{
|
||||
"Unix to DOS",
|
||||
"first line\nsecond line\n",
|
||||
"\r\n",
|
||||
"first line\r\nsecond line\r\n",
|
||||
},
|
||||
{
|
||||
"Unix to Unix",
|
||||
"first line\nsecond line\n",
|
||||
"\n",
|
||||
"first line\nsecond line\n",
|
||||
},
|
||||
{
|
||||
"Unix to Mac",
|
||||
"first line\nsecond line\n",
|
||||
"\r",
|
||||
"first line\rsecond line\r",
|
||||
},
|
||||
{
|
||||
"DOS to Unix",
|
||||
"first line\r\nsecond line\r\n",
|
||||
"\n",
|
||||
"first line\nsecond line\n",
|
||||
},
|
||||
{
|
||||
"DOS to DOS",
|
||||
"first line\r\nsecond line\r\n",
|
||||
"\r\n",
|
||||
"first line\r\nsecond line\r\n",
|
||||
},
|
||||
{
|
||||
"DOS to OldMac",
|
||||
"first line\r\nsecond line\r\n",
|
||||
"\r",
|
||||
"first line\rsecond line\r",
|
||||
},
|
||||
{
|
||||
"Mac to DOS",
|
||||
"first line\rsecond line\r",
|
||||
"\r\n",
|
||||
"first line\r\nsecond line\r\n",
|
||||
},
|
||||
{
|
||||
"Mac to Unix",
|
||||
"first line\rsecond line\r",
|
||||
"\n",
|
||||
"first line\nsecond line\n",
|
||||
},
|
||||
{
|
||||
"DOS, Mac to Unix",
|
||||
"first line\r\n\rsecond line\r",
|
||||
"\n",
|
||||
"first line\n\nsecond line\n",
|
||||
},
|
||||
{
|
||||
"Mac, DOS to Unix",
|
||||
"first line\r\r\r\nsecond line\r",
|
||||
"\n",
|
||||
"first line\n\n\nsecond line\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
c := &contentToAdd{content: tc.InputContent}
|
||||
want := tc.WantedContent
|
||||
|
||||
got := c.Content(tc.TargetNewline)
|
||||
|
||||
assert.Equal(t, want, got)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,6 +24,10 @@ import (
|
||||
|
||||
const UserValuePrefix = "YOUR_"
|
||||
|
||||
const windowsNewline = "\r\n"
|
||||
const unixNewline = "\n"
|
||||
const oldMacNewline = "\r"
|
||||
|
||||
func NewFixHandler(fixInfo *metav1.FixInfo) (*FixHandler, error) {
|
||||
jsonFile, err := os.Open(fixInfo.ReportFile)
|
||||
if err != nil {
|
||||
@@ -232,7 +236,9 @@ func (h *FixHandler) getFilePathAndIndex(filePathWithIndex string) (filePath str
|
||||
}
|
||||
|
||||
func (h *FixHandler) ApplyFixToContent(yamlAsString, yamlExpression string) (fixedString string, err error) {
|
||||
yamlLines := strings.Split(yamlAsString, "\n")
|
||||
newline := determineNewlineSeparator(yamlAsString)
|
||||
|
||||
yamlLines := strings.Split(yamlAsString, newline)
|
||||
|
||||
originalRootNodes, err := decodeDocumentRoots(yamlAsString)
|
||||
|
||||
@@ -248,9 +254,9 @@ func (h *FixHandler) ApplyFixToContent(yamlAsString, yamlExpression string) (fix
|
||||
|
||||
fileFixInfo := getFixInfo(originalRootNodes, fixedRootNodes)
|
||||
|
||||
fixedYamlLines := getFixedYamlLines(yamlLines, fileFixInfo)
|
||||
fixedYamlLines := getFixedYamlLines(yamlLines, fileFixInfo, newline)
|
||||
|
||||
fixedString = getStringFromSlice(fixedYamlLines)
|
||||
fixedString = getStringFromSlice(fixedYamlLines, newline)
|
||||
|
||||
return fixedString, nil
|
||||
}
|
||||
@@ -344,3 +350,12 @@ func writeFixesToFile(filepath, content string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func determineNewlineSeparator(contents string) string {
|
||||
switch {
|
||||
case strings.Contains(contents, windowsNewline):
|
||||
return windowsNewline
|
||||
default:
|
||||
return unixNewline
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,11 @@ func getTestCases() []indentationTestCase {
|
||||
`select(di==0).spec.containers += {"name": "redis", "image": "redis"}`,
|
||||
"inserts/tc-10-01-expected.yaml",
|
||||
},
|
||||
{
|
||||
"inserts/tc-11-00-input-list-insert-new-mapping-crlf-newlines.yaml",
|
||||
`select(di==0).spec.containers += {"name": "redis", "image": "redis"}`,
|
||||
"inserts/tc-11-01-expected.yaml",
|
||||
},
|
||||
|
||||
// Removal Scenarios
|
||||
{
|
||||
|
||||
11
core/pkg/fixhandler/testdata/inserts/tc-11-00-input-list-insert-new-mapping-crlf-newlines.yaml
vendored
Normal file
11
core/pkg/fixhandler/testdata/inserts/tc-11-00-input-list-insert-new-mapping-crlf-newlines.yaml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# Fix to Apply:
|
||||
# select(di==0).spec.containers += {"name": "redis", "image": "redis"}
|
||||
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: indented-list-insert-new-object
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx_container
|
||||
image: nginx
|
||||
13
core/pkg/fixhandler/testdata/inserts/tc-11-01-expected.yaml
vendored
Normal file
13
core/pkg/fixhandler/testdata/inserts/tc-11-01-expected.yaml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# Fix to Apply:
|
||||
# select(di==0).spec.containers += {"name": "redis", "image": "redis"}
|
||||
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: indented-list-insert-new-object
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx_container
|
||||
image: nginx
|
||||
- name: redis
|
||||
image: redis
|
||||
@@ -244,7 +244,7 @@ func removeNewLinesAtTheEnd(yamlLines []string) []string {
|
||||
return yamlLines
|
||||
}
|
||||
|
||||
func getFixedYamlLines(yamlLines []string, fileFixInfo fileFixInfo) (fixedYamlLines []string) {
|
||||
func getFixedYamlLines(yamlLines []string, fileFixInfo fileFixInfo, newline string) (fixedYamlLines []string) {
|
||||
|
||||
// Determining last line requires original yaml lines slice. The placeholder for last line is replaced with the real last line
|
||||
assignLastLine(fileFixInfo.contentsToAdd, fileFixInfo.linesToRemove, &yamlLines)
|
||||
@@ -267,7 +267,7 @@ func getFixedYamlLines(yamlLines []string, fileFixInfo fileFixInfo) (fixedYamlLi
|
||||
lineIdx += 1
|
||||
}
|
||||
|
||||
content := (*fileFixInfo.contentsToAdd)[lineToAddIdx].content
|
||||
content := (*fileFixInfo.contentsToAdd)[lineToAddIdx].Content(newline)
|
||||
fixedYamlLines = append(fixedYamlLines, content)
|
||||
|
||||
lineToAddIdx += 1
|
||||
|
||||
@@ -401,6 +401,6 @@ func updateTracker(nodeList *[]nodeInfo, tracker int) int {
|
||||
return updatedTracker
|
||||
}
|
||||
|
||||
func getStringFromSlice(yamlLines []string) (fixedYamlString string) {
|
||||
return strings.Join(yamlLines, "\n")
|
||||
func getStringFromSlice(yamlLines []string, newline string) (fixedYamlString string) {
|
||||
return strings.Join(yamlLines, newline)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user