Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(oscal): link remapper method component defn #879

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/pkg/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
goversion "github.com/hashicorp/go-version"
"k8s.io/apimachinery/pkg/util/yaml"

"github.com/defenseunicorns/lula/src/pkg/common/network"
"github.com/defenseunicorns/lula/src/pkg/domains/api"
"github.com/defenseunicorns/lula/src/pkg/domains/files"
kube "github.com/defenseunicorns/lula/src/pkg/domains/kubernetes"
Expand Down Expand Up @@ -211,3 +212,39 @@ func CleanMultilineString(str string) string {
formatted := re.ReplaceAllString(str, "\n")
return formatted
}

// RemapPath takes an input path, relative to the baseDir, and remaps it to be relative to the newDir
// Example: path = "folder/file.txt", baseDir = "/home/user/dir", newDir = "/home/user/newDir"
// output path = "../dir/folder/file.txt"
func RemapPath(path string, baseDir string, newDir string) (string, error) {
// Do nothing if the path is a UUID reference
if isUUIDReference(path) {
return path, nil
}

// Return if the path is a URL or absolute link
localDir := network.GetLocalFileDir(path, baseDir)
if localDir == "" {
return path, nil
}

// Trim file:// or file:, if present
path = strings.TrimPrefix(strings.TrimPrefix(path, "file://"), "file:")

// Find the relative path from newDir to baseDir
relativePath, err := filepath.Rel(newDir, baseDir)
if err != nil {
return "", err
}

// Append the original relative path to the computed relative path
remappedPath := filepath.Join(relativePath, path)
remappedPath = filepath.Clean(remappedPath)

return remappedPath, nil
}

func isUUIDReference(path string) bool {
path = strings.TrimPrefix(path, UUID_PREFIX)
return checkValidUuid(path)
}
107 changes: 107 additions & 0 deletions src/pkg/common/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,113 @@ func TestIsVersionValid(t *testing.T) {
}
}

func TestRemapPath(t *testing.T) {
tests := []struct {
name string
path string
baseDir string
newDir string
expectedPath string
expectedError bool
}{
{
name: "Absolute Path",
path: "/path/to/file.txt",
baseDir: "/path/to",
newDir: "/new/path",
expectedPath: "/path/to/file.txt",
expectedError: false,
},
{
name: "Relative Path",
path: "path/to/file.txt",
baseDir: "/app",
newDir: "/app2",
expectedPath: "../app/path/to/file.txt",
expectedError: false,
},
{
name: "Relative Path with ..",
path: "../path/to/file.txt",
baseDir: "/app/sub-path",
newDir: "/app",
expectedPath: "path/to/file.txt",
expectedError: false,
},
{
name: "Relative Path with deep nesting",
path: "../../file.txt",
baseDir: "/app/path/to/caller",
newDir: "/app",
expectedPath: "path/file.txt",
expectedError: false,
},
{
name: "Relative Path with file://",
path: "file://path/to/file.txt",
baseDir: "/app",
newDir: "/app2",
expectedPath: "../app/path/to/file.txt",
expectedError: false,
},
{
name: "Relative Path with file:",
path: "file:file.txt",
baseDir: "/app",
newDir: "/app2",
expectedPath: "../app/file.txt",
expectedError: false,
},
{
name: "Absolute Path with file://",
path: "file:///path/to/file.txt",
baseDir: "/path/to",
newDir: "/new/path",
expectedPath: "file:///path/to/file.txt",
expectedError: false,
},
{
name: "UUID",
path: "#0a2b9722-06ce-446f-85e5-cdfe2fe70975",
baseDir: "/path/to",
newDir: "/new/path",
expectedPath: "#0a2b9722-06ce-446f-85e5-cdfe2fe70975",
expectedError: false,
},
{
// This doesn't error because there's no check in the function to validate the path
// I think this is ok, because the old path wouldn't work anyway, so the new path not working doesn't matter(?)
name: "Invalid path remap",
path: "../../path/to/file.txt",
baseDir: "/app",
newDir: "/app2",
expectedPath: "../../path/to/file.txt",
expectedError: false,
},
{
// Similar logic to previous case: This doesn't error because there's no check in the function to validate the path
name: "Invalid path name",
path: "inv@1*d pa#h",
baseDir: "/app",
newDir: "/app2",
expectedPath: "../app/inv@1*d pa#h",
expectedError: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotPath, err := common.RemapPath(tt.path, tt.baseDir, tt.newDir)
if (err != nil) != tt.expectedError {
t.Errorf("RemapPath() error = %v, wantErr %v", err, tt.expectedError)
return
}
require.Equal(t, tt.expectedPath, gotPath)
})
}

}

func FuzzPrefix(f *testing.F) {
f.Add("uuid")
f.Add("149f0049-7a3c-4e4d-8431-bec3a55f31d9")
Expand Down
42 changes: 31 additions & 11 deletions src/pkg/common/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,25 +156,45 @@ func FetchLocalFile(url *url.URL, config *fetchOpts) ([]byte, error) {
return bytes, err
}

// GetLocalFileDir returns the directory of a local file
// See URL for reference: https://pkg.go.dev/net/url#URL
// Intent of check is to handle different specifications of file paths:
// - file:///path/to/file
// - file:my-file.txt
// - ./path/to/file
// - /path/to/file
// - https://example.com/path/to/file
// ** This will not work for Windows file paths, but Lula doesn't run on Windows for now
func GetLocalFileDir(inputURL, baseDir string) string {
url, err := url.Parse(inputURL)
if err != nil {
return ""
}
requestUri := url.RequestURI()

// Intent of check is to handle different specifications of file paths:
// - file:///path/to/file
// - ./path/to/file
// - /path/to/file
// - https://example.com/path/to/file
if url.Scheme == "file" || !url.IsAbs() {
fullPath := filepath.Join(baseDir, url.Host, requestUri)
if _, err := os.Stat(fullPath); err == nil {
return filepath.Dir(fullPath)
if url.Scheme == "file" {
// If the scheme is file, check if the path is absolute
// To check absolute path, check both the host and the opaque fields
if url.Opaque != "" {
return handlePath(url.Opaque, baseDir)
}
if url.Host == "" {
return ""
}
} else if url.Scheme != "" {
return ""
}
return ""

return handlePath(filepath.Join(url.Host, url.RequestURI()), baseDir)
}

func handlePath(path, baseDir string) string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method name isn'g telling me anything, and it's not obvious to me why it returns an empty string if given an abs path but a full path to a directory if given a relative path. Can you add some comments that explains why this is the behavior? And maybe rename it - returnBaseDirIfNotAbsPath seems like a more accurate description of what's happening (though I still wonder why)

if filepath.IsAbs(path) {
return ""
}

fullPath := filepath.Join(baseDir, path)

return filepath.Dir(fullPath)
}

// ValidateChecksum validates a given checksum against a given []bytes.
Expand Down
73 changes: 73 additions & 0 deletions src/pkg/common/network/network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,3 +224,76 @@ func TestParseChecksum(t *testing.T) {
})
}
}

func TestGetLocalFileDir(t *testing.T) {
tests := []struct {
name string
inputFile string
baseDir string
expectedDir string
}{
{
name: "Absolute Path",
inputFile: "/root/path/to/file.txt",
baseDir: "/root",
expectedDir: "",
},
{
name: "Relative Path",
inputFile: "path/to/file.txt",
baseDir: "/root",
expectedDir: "/root/path/to",
},
{
name: "http URL",
inputFile: "https://example.com/path/to/file.txt",
baseDir: "/path/to",
expectedDir: "",
},
{
name: "Relative Path with ..",
inputFile: "../path/to/file.txt",
baseDir: "/root/path",
expectedDir: "/root/path/to",
},
{
name: "Relative Path with file://",
inputFile: "file://path/to/file.txt",
baseDir: "/root",
expectedDir: "/root/path/to",
},
{
name: "Relative Path with file://..",
inputFile: "file://../path/to/file.txt",
baseDir: "/root/path",
expectedDir: "/root/path/to",
},
{
name: "Absolute Path with file://",
inputFile: "file:///root/path/to/file.txt",
baseDir: "/root",
expectedDir: "",
},
{
name: "Opaque absolute Path",
inputFile: "file:/root/path/to/file.txt",
baseDir: "/root",
expectedDir: "",
},
{
name: "Opaque relative Path",
inputFile: "file:to/file.txt",
baseDir: "/root/path",
expectedDir: "/root/path/to",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotDir := network.GetLocalFileDir(tt.inputFile, tt.baseDir)
if tt.expectedDir != gotDir {
t.Errorf("GetLocalFileDir() gotDir = %v, want %v", gotDir, tt.expectedDir)
}
})
}
}
Loading
Loading