feat: add git-explorer MCP server for read-only repository access
Implements a new MCP server that provides read-only access to git repositories using go-git. Designed for deployment verification by comparing deployed flake revisions against source repositories. 9 tools: resolve_ref, get_log, get_commit_info, get_diff_files, get_file_at_commit, is_ancestor, commits_between, list_branches, search_commits. Includes CLI commands, NixOS module, and comprehensive tests. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
91
internal/gitexplorer/validation_test.go
Normal file
91
internal/gitexplorer/validation_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package gitexplorer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValidatePath(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
path string
|
||||
wantErr error
|
||||
}{
|
||||
// Valid paths
|
||||
{
|
||||
name: "simple file",
|
||||
path: "README.md",
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "nested file",
|
||||
path: "internal/gitexplorer/types.go",
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "file with dots",
|
||||
path: "file.test.go",
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "current dir prefix",
|
||||
path: "./README.md",
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "deeply nested",
|
||||
path: "a/b/c/d/e/f/g.txt",
|
||||
wantErr: nil,
|
||||
},
|
||||
|
||||
// Invalid paths
|
||||
{
|
||||
name: "empty path",
|
||||
path: "",
|
||||
wantErr: ErrEmptyPath,
|
||||
},
|
||||
{
|
||||
name: "absolute path unix",
|
||||
path: "/etc/passwd",
|
||||
wantErr: ErrAbsolutePath,
|
||||
},
|
||||
{
|
||||
name: "parent dir traversal simple",
|
||||
path: "../secret.txt",
|
||||
wantErr: ErrPathTraversal,
|
||||
},
|
||||
{
|
||||
name: "parent dir traversal nested",
|
||||
path: "foo/../../../etc/passwd",
|
||||
wantErr: ErrPathTraversal,
|
||||
},
|
||||
{
|
||||
name: "parent dir traversal in middle",
|
||||
path: "foo/bar/../../../secret",
|
||||
wantErr: ErrPathTraversal,
|
||||
},
|
||||
{
|
||||
name: "null byte",
|
||||
path: "file\x00.txt",
|
||||
wantErr: ErrNullByte,
|
||||
},
|
||||
{
|
||||
name: "null byte in middle",
|
||||
path: "foo/bar\x00baz/file.txt",
|
||||
wantErr: ErrNullByte,
|
||||
},
|
||||
{
|
||||
name: "double dot only",
|
||||
path: "..",
|
||||
wantErr: ErrPathTraversal,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := ValidatePath(tt.path)
|
||||
if err != tt.wantErr {
|
||||
t.Errorf("ValidatePath(%q) = %v, want %v", tt.path, err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user