mirror of
https://github.com/cexll/myclaude.git
synced 2026-03-01 15:03:45 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62309d1429 | ||
|
|
33a94d2bc4 |
@@ -125,6 +125,9 @@ func TestEnvInjection_LogsToStderrAndMasksKey(t *testing.T) {
|
|||||||
if cmd.env["ANTHROPIC_API_KEY"] != apiKey {
|
if cmd.env["ANTHROPIC_API_KEY"] != apiKey {
|
||||||
t.Fatalf("ANTHROPIC_API_KEY=%q, want %q", cmd.env["ANTHROPIC_API_KEY"], apiKey)
|
t.Fatalf("ANTHROPIC_API_KEY=%q, want %q", cmd.env["ANTHROPIC_API_KEY"], apiKey)
|
||||||
}
|
}
|
||||||
|
if cmd.env["CLAUDE_CODE_TMPDIR"] == "" {
|
||||||
|
t.Fatalf("expected CLAUDE_CODE_TMPDIR to be set for nested claude, got empty")
|
||||||
|
}
|
||||||
|
|
||||||
if !strings.Contains(got, "Env: ANTHROPIC_BASE_URL="+baseURL) {
|
if !strings.Contains(got, "Env: ANTHROPIC_BASE_URL="+baseURL) {
|
||||||
t.Fatalf("stderr missing base URL env log; stderr=%q", got)
|
t.Fatalf("stderr missing base URL env log; stderr=%q", got)
|
||||||
@@ -132,4 +135,7 @@ func TestEnvInjection_LogsToStderrAndMasksKey(t *testing.T) {
|
|||||||
if !strings.Contains(got, "Env: ANTHROPIC_API_KEY=eyJh****test") {
|
if !strings.Contains(got, "Env: ANTHROPIC_API_KEY=eyJh****test") {
|
||||||
t.Fatalf("stderr missing masked API key log; stderr=%q", got)
|
t.Fatalf("stderr missing masked API key log; stderr=%q", got)
|
||||||
}
|
}
|
||||||
|
if !strings.Contains(got, "CLAUDE_CODE_TMPDIR: ") {
|
||||||
|
t.Fatalf("stderr missing CLAUDE_CODE_TMPDIR log; stderr=%q", got)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1154,10 +1154,23 @@ func RunCodexTaskWithContext(parentCtx context.Context, taskSpec TaskSpec, backe
|
|||||||
|
|
||||||
injectTempEnv(cmd)
|
injectTempEnv(cmd)
|
||||||
|
|
||||||
|
if commandName == "claude" {
|
||||||
|
// Claude 2.1.45+ calls Nz7() on startup to clean its tasks directory,
|
||||||
|
// which deletes the parent session's *.output files and causes "(no output)".
|
||||||
|
// Assign each nested claude its own isolated tmpdir so it only cleans its own files.
|
||||||
|
nestedTmpDir, err := os.MkdirTemp("", fmt.Sprintf("cc-nested-%d-", os.Getpid()))
|
||||||
|
if err != nil {
|
||||||
|
logWarnFn("Failed to create isolated CLAUDE_CODE_TMPDIR: " + err.Error())
|
||||||
|
} else {
|
||||||
|
cmd.SetEnv(map[string]string{"CLAUDE_CODE_TMPDIR": nestedTmpDir})
|
||||||
|
defer os.RemoveAll(nestedTmpDir) //nolint:errcheck
|
||||||
|
logInfoFn("CLAUDE_CODE_TMPDIR: " + nestedTmpDir)
|
||||||
|
fmt.Fprintln(os.Stderr, " CLAUDE_CODE_TMPDIR: "+nestedTmpDir)
|
||||||
|
}
|
||||||
|
|
||||||
// Claude Code sets CLAUDECODE=1 in its child processes. If we don't
|
// Claude Code sets CLAUDECODE=1 in its child processes. If we don't
|
||||||
// remove it, the spawned `claude -p` detects the variable and refuses
|
// remove it, the spawned `claude -p` detects the variable and refuses
|
||||||
// to start ("cannot be launched inside another Claude Code session").
|
// to start ("cannot be launched inside another Claude Code session").
|
||||||
if commandName == "claude" {
|
|
||||||
cmd.UnsetEnv("CLAUDECODE")
|
cmd.UnsetEnv("CLAUDECODE")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -569,11 +569,17 @@ func isUnsafeFile(path string, tempDir string) (bool, string) {
|
|||||||
return true, fmt.Sprintf("path resolution failed: %v", err)
|
return true, fmt.Sprintf("path resolution failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get absolute path of tempDir
|
// Get canonical path of tempDir, resolving symlinks to match resolvedPath.
|
||||||
absTempDir, err := filepath.Abs(tempDir)
|
// On macOS, os.TempDir() returns /var/folders/... but EvalSymlinks resolves
|
||||||
|
// files to /private/var/folders/..., causing a spurious "outside tempDir" mismatch.
|
||||||
|
absTempDir, err := evalSymlinksFn(tempDir)
|
||||||
|
if err != nil {
|
||||||
|
// Fallback to Abs if symlink resolution fails
|
||||||
|
absTempDir, err = filepath.Abs(tempDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true, fmt.Sprintf("tempDir resolution failed: %v", err)
|
return true, fmt.Sprintf("tempDir resolution failed: %v", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure resolved path is within tempDir
|
// Ensure resolved path is within tempDir
|
||||||
relPath, err := filepath.Rel(absTempDir, resolvedPath)
|
relPath, err := filepath.Rel(absTempDir, resolvedPath)
|
||||||
|
|||||||
@@ -515,7 +515,10 @@ func TestLoggerIsUnsafeFileSecurityChecks(t *testing.T) {
|
|||||||
return fakeFileInfo{}, nil
|
return fakeFileInfo{}, nil
|
||||||
})
|
})
|
||||||
outside := filepath.Join(filepath.Dir(absTempDir), "etc", "passwd")
|
outside := filepath.Join(filepath.Dir(absTempDir), "etc", "passwd")
|
||||||
stubEvalSymlinks(t, func(string) (string, error) {
|
stubEvalSymlinks(t, func(p string) (string, error) {
|
||||||
|
if p == tempDir {
|
||||||
|
return absTempDir, nil
|
||||||
|
}
|
||||||
return outside, nil
|
return outside, nil
|
||||||
})
|
})
|
||||||
unsafe, reason := isUnsafeFile(filepath.Join("..", "..", "etc", "passwd"), tempDir)
|
unsafe, reason := isUnsafeFile(filepath.Join("..", "..", "etc", "passwd"), tempDir)
|
||||||
@@ -529,16 +532,73 @@ func TestLoggerIsUnsafeFileSecurityChecks(t *testing.T) {
|
|||||||
return fakeFileInfo{}, nil
|
return fakeFileInfo{}, nil
|
||||||
})
|
})
|
||||||
otherDir := t.TempDir()
|
otherDir := t.TempDir()
|
||||||
stubEvalSymlinks(t, func(string) (string, error) {
|
outsidePath := filepath.Join(otherDir, "codeagent-wrapper-9.log")
|
||||||
return filepath.Join(otherDir, "codeagent-wrapper-9.log"), nil
|
stubEvalSymlinks(t, func(p string) (string, error) {
|
||||||
|
if p == tempDir {
|
||||||
|
return absTempDir, nil
|
||||||
|
}
|
||||||
|
return outsidePath, nil
|
||||||
})
|
})
|
||||||
unsafe, reason := isUnsafeFile(filepath.Join(otherDir, "codeagent-wrapper-9.log"), tempDir)
|
unsafe, reason := isUnsafeFile(outsidePath, tempDir)
|
||||||
if !unsafe || reason != "file is outside tempDir" {
|
if !unsafe || reason != "file is outside tempDir" {
|
||||||
t.Fatalf("expected outside file to be rejected, got unsafe=%v reason=%q", unsafe, reason)
|
t.Fatalf("expected outside file to be rejected, got unsafe=%v reason=%q", unsafe, reason)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoggerIsUnsafeFileCanonicalizesTempDir(t *testing.T) {
|
||||||
|
stubFileStat(t, func(string) (os.FileInfo, error) {
|
||||||
|
return fakeFileInfo{}, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
tempDir := filepath.FromSlash("/var/folders/abc/T")
|
||||||
|
canonicalTempDir := filepath.FromSlash("/private/var/folders/abc/T")
|
||||||
|
logPath := filepath.Join(tempDir, "codeagent-wrapper-1.log")
|
||||||
|
canonicalLogPath := filepath.Join(canonicalTempDir, "codeagent-wrapper-1.log")
|
||||||
|
|
||||||
|
stubEvalSymlinks(t, func(p string) (string, error) {
|
||||||
|
switch p {
|
||||||
|
case tempDir:
|
||||||
|
return canonicalTempDir, nil
|
||||||
|
case logPath:
|
||||||
|
return canonicalLogPath, nil
|
||||||
|
default:
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
unsafe, reason := isUnsafeFile(logPath, tempDir)
|
||||||
|
if unsafe {
|
||||||
|
t.Fatalf("expected canonicalized tempDir to be accepted, got unsafe=%v reason=%q", unsafe, reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoggerIsUnsafeFileFallsBackToAbsOnTempDirEvalFailure(t *testing.T) {
|
||||||
|
stubFileStat(t, func(string) (os.FileInfo, error) {
|
||||||
|
return fakeFileInfo{}, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
absTempDir, err := filepath.Abs(tempDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("filepath.Abs() error = %v", err)
|
||||||
|
}
|
||||||
|
logPath := filepath.Join(tempDir, "codeagent-wrapper-1.log")
|
||||||
|
absLogPath := filepath.Join(absTempDir, "codeagent-wrapper-1.log")
|
||||||
|
|
||||||
|
stubEvalSymlinks(t, func(p string) (string, error) {
|
||||||
|
if p == tempDir {
|
||||||
|
return "", errors.New("boom")
|
||||||
|
}
|
||||||
|
return absLogPath, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
unsafe, reason := isUnsafeFile(logPath, tempDir)
|
||||||
|
if unsafe {
|
||||||
|
t.Fatalf("expected Abs fallback to allow file, got unsafe=%v reason=%q", unsafe, reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestLoggerPathAndRemove(t *testing.T) {
|
func TestLoggerPathAndRemove(t *testing.T) {
|
||||||
setTempDirEnv(t, t.TempDir())
|
setTempDirEnv(t, t.TempDir())
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user