chore: housekeeping and refactoring (bump to 4.7.1)

- Remove unused parameters, dead files, and inaccurate doc.go files
- Extract shared helpers, eliminate duplication
- Rename cheatpath.Cheatpath to cheatpath.Path
- Optimize filesystem walks (WalkDir, skip .git)
- Move sheet name validation to sheet.Validate
- Move integration tests to test/integration/
- Consolidate internal/mock into mocks/
- Move fuzz.sh to test/
- Inline loadSheets helper into command callers
- Extract config.New into its own file
- Fix stale references in HACKING.md and CLAUDE.md
- Restore plan9 build target
- Remove redundant and low-value tests
- Clean up project documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Christopher Allen Lane
2026-02-15 15:09:30 -05:00
parent d4a8a79628
commit 5ad1a3c39f
68 changed files with 605 additions and 1578 deletions

View File

@@ -0,0 +1,229 @@
package integration
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
)
// TestPathTraversalIntegration tests that the cheat binary properly blocks
// path traversal attempts when invoked as a subprocess.
func TestPathTraversalIntegration(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("integration test uses Unix-specific env and tools")
}
// Build the cheat binary
binPath := filepath.Join(t.TempDir(), "cheat_test")
build := exec.Command("go", "build", "-o", binPath, "./cmd/cheat")
build.Dir = repoRoot(t)
if output, err := build.CombinedOutput(); err != nil {
t.Fatalf("Failed to build cheat: %v\nOutput: %s", err, output)
}
// Set up test environment
testDir := t.TempDir()
sheetsDir := filepath.Join(testDir, "sheets")
os.MkdirAll(sheetsDir, 0755)
// Create config
config := fmt.Sprintf(`---
editor: echo
colorize: false
pager: cat
cheatpaths:
- name: test
path: %s
readonly: false
`, sheetsDir)
configPath := filepath.Join(testDir, "config.yml")
if err := os.WriteFile(configPath, []byte(config), 0644); err != nil {
t.Fatalf("Failed to write config: %v", err)
}
// Test table
tests := []struct {
name string
command []string
wantFail bool
wantMsg string
}{
// Blocked patterns
{
name: "block parent traversal edit",
command: []string{"--edit", "../evil"},
wantFail: true,
wantMsg: "cannot contain '..'",
},
{
name: "block absolute path edit",
command: []string{"--edit", "/etc/passwd"},
wantFail: true,
wantMsg: "cannot be an absolute path",
},
{
name: "block home dir edit",
command: []string{"--edit", "~/.ssh/config"},
wantFail: true,
wantMsg: "cannot start with '~'",
},
{
name: "block parent traversal remove",
command: []string{"--rm", "../evil"},
wantFail: true,
wantMsg: "cannot contain '..'",
},
{
name: "block complex traversal",
command: []string{"--edit", "foo/../../bar"},
wantFail: true,
wantMsg: "cannot contain '..'",
},
{
name: "block just dots",
command: []string{"--edit", ".."},
wantFail: true,
wantMsg: "cannot contain '..'",
},
{
name: "block empty name",
command: []string{"--edit", ""},
wantFail: true,
wantMsg: "cannot be empty",
},
// Allowed patterns
{
name: "allow simple name",
command: []string{"--edit", "docker"},
wantFail: false,
},
{
name: "allow nested name",
command: []string{"--edit", "lang/go"},
wantFail: false,
},
{
name: "block hidden file",
command: []string{"--edit", ".gitignore"},
wantFail: true,
wantMsg: "cannot start with '.'",
},
{
name: "allow current dir",
command: []string{"--edit", "./local"},
wantFail: false,
},
}
// Run tests
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
cmd := exec.Command(binPath, tc.command...)
cmd.Env = []string{
fmt.Sprintf("CHEAT_CONFIG_PATH=%s", configPath),
fmt.Sprintf("HOME=%s", testDir),
}
output, err := cmd.CombinedOutput()
if tc.wantFail {
if err == nil {
t.Errorf("Expected failure but command succeeded. Output: %s", output)
}
if !strings.Contains(string(output), "invalid cheatsheet name") {
t.Errorf("Expected 'invalid cheatsheet name' error, got: %s", output)
}
if tc.wantMsg != "" && !strings.Contains(string(output), tc.wantMsg) {
t.Errorf("Expected message %q in output, got: %s", tc.wantMsg, output)
}
} else {
// Command might fail for other reasons (e.g., editor not found)
// but should NOT fail with "invalid cheatsheet name"
if strings.Contains(string(output), "invalid cheatsheet name") {
t.Errorf("Command incorrectly blocked. Output: %s", output)
}
}
})
}
}
// TestPathTraversalRealWorld tests with more realistic scenarios
func TestPathTraversalRealWorld(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("integration test uses Unix-specific env and tools")
}
// This test ensures our protection works with actual file operations
// Build cheat
binPath := filepath.Join(t.TempDir(), "cheat_test")
build := exec.Command("go", "build", "-o", binPath, "./cmd/cheat")
build.Dir = repoRoot(t)
if output, err := build.CombinedOutput(); err != nil {
t.Fatalf("Failed to build: %v\n%s", err, output)
}
// Create test structure
testRoot := t.TempDir()
sheetsDir := filepath.Join(testRoot, "cheatsheets")
secretDir := filepath.Join(testRoot, "secrets")
os.MkdirAll(sheetsDir, 0755)
os.MkdirAll(secretDir, 0755)
// Create a "secret" file that should not be accessible
secretFile := filepath.Join(secretDir, "secret.txt")
os.WriteFile(secretFile, []byte("SECRET DATA"), 0644)
// Create config using vim in non-interactive mode
config := fmt.Sprintf(`---
editor: vim -u NONE -n --cmd "set noswapfile" --cmd "wq"
colorize: false
pager: cat
cheatpaths:
- name: personal
path: %s
readonly: false
`, sheetsDir)
configPath := filepath.Join(testRoot, "config.yml")
os.WriteFile(configPath, []byte(config), 0644)
// Test 1: Try to edit a file outside cheatsheets using traversal
cmd := exec.Command(binPath, "--edit", "../secrets/secret")
cmd.Env = []string{
fmt.Sprintf("CHEAT_CONFIG_PATH=%s", configPath),
fmt.Sprintf("HOME=%s", testRoot),
}
output, err := cmd.CombinedOutput()
if err == nil || !strings.Contains(string(output), "invalid cheatsheet name") {
t.Errorf("Path traversal was not blocked! Output: %s", output)
}
// Test 2: Verify the secret file is still intact
content, _ := os.ReadFile(secretFile)
if string(content) != "SECRET DATA" {
t.Errorf("Secret file was modified!")
}
// Test 3: Verify no files were created outside sheets directory
err = filepath.Walk(testRoot, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() &&
path != configPath &&
path != secretFile &&
!strings.HasPrefix(path, sheetsDir) {
t.Errorf("File created outside allowed directory: %s", path)
}
return nil
})
if err != nil {
t.Errorf("Walk error: %v", err)
}
}