Skip to content

Commit a24247b

Browse files
committed
Updates for compatibility with main-merged PRs
1 parent b75b0db commit a24247b

File tree

3 files changed

+134
-4
lines changed

3 files changed

+134
-4
lines changed

client/internal/testing/runner.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@ func (r *Runner) runSingleTest(toolName, testCase, toolDir string) {
223223
// Resolve {{tmpdir}} placeholders in all params
224224
params = resolvePathPlaceholders(params, r.tmpBase)
225225

226+
// Clean up stale interpretedOutput from prior test runs so that
227+
// directory comparisons only see output from this invocation.
228+
cleanStaleOutput(toolName, params, ".")
229+
226230
// Call the tool (using server tool name which may differ from fixture dir name)
227231
content, isError, callErr := r.caller.CallToolRaw(serverToolName, params)
228232
elapsed := time.Since(start)
@@ -438,6 +442,37 @@ func fileExists(path string) bool {
438442
return err == nil
439443
}
440444

445+
// cleanStaleOutput removes stale interpretedOutput files or directories from
446+
// prior codeql_query_run test invocations. This prevents stale results from
447+
// affecting directory comparisons. Only relative paths without directory
448+
// traversals are cleaned (CWE-22 prevention).
449+
func cleanStaleOutput(toolName string, params map[string]any, baseDir string) {
450+
if toolName != "codeql_query_run" {
451+
return
452+
}
453+
outputVal, ok := params["interpretedOutput"]
454+
if !ok {
455+
return
456+
}
457+
outputPath, ok := outputVal.(string)
458+
if !ok || outputPath == "" {
459+
return
460+
}
461+
462+
normalized := filepath.Clean(outputPath)
463+
464+
// Reject absolute paths and directory traversals (CWE-22).
465+
if filepath.IsAbs(normalized) ||
466+
strings.HasPrefix(normalized, ".."+string(filepath.Separator)) ||
467+
normalized == ".." {
468+
fmt.Fprintf(os.Stderr, " Skipping interpretedOutput cleanup: unsafe path %q\n", outputPath)
469+
return
470+
}
471+
472+
fullPath := filepath.Join(baseDir, normalized)
473+
os.RemoveAll(fullPath)
474+
}
475+
441476
func truncate(s string, max int) string {
442477
s = strings.ReplaceAll(s, "\n", " ")
443478
if len(s) > max {

client/internal/testing/runner_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,3 +365,98 @@ func TestRunnerAssertionFailure(t *testing.T) {
365365
t.Error("mock_tool/assertion_test result not found")
366366
}
367367
}
368+
369+
func TestCleanStaleOutputRelativeFile(t *testing.T) {
370+
dir := t.TempDir()
371+
staleFile := filepath.Join(dir, "query-results.sarif")
372+
os.WriteFile(staleFile, []byte("stale"), 0o600)
373+
374+
params := map[string]any{
375+
"interpretedOutput": "query-results.sarif",
376+
}
377+
378+
cleanStaleOutput("codeql_query_run", params, dir)
379+
380+
if fileExists(staleFile) {
381+
t.Error("expected stale file to be removed")
382+
}
383+
}
384+
385+
func TestCleanStaleOutputRelativeDir(t *testing.T) {
386+
dir := t.TempDir()
387+
staleDir := filepath.Join(dir, "query-results")
388+
os.MkdirAll(filepath.Join(staleDir, "subdir"), 0o755)
389+
os.WriteFile(filepath.Join(staleDir, "subdir", "file.txt"), []byte("stale"), 0o600)
390+
391+
params := map[string]any{
392+
"interpretedOutput": "query-results",
393+
}
394+
395+
cleanStaleOutput("codeql_query_run", params, dir)
396+
397+
if fileExists(staleDir) {
398+
t.Error("expected stale directory to be removed")
399+
}
400+
}
401+
402+
func TestCleanStaleOutputRejectsAbsolutePath(t *testing.T) {
403+
dir := t.TempDir()
404+
absFile := filepath.Join(dir, "safe-file")
405+
os.WriteFile(absFile, []byte("keep"), 0o600)
406+
407+
params := map[string]any{
408+
"interpretedOutput": absFile, // absolute path
409+
}
410+
411+
cleanStaleOutput("codeql_query_run", params, dir)
412+
413+
if !fileExists(absFile) {
414+
t.Error("absolute path should NOT be removed")
415+
}
416+
}
417+
418+
func TestCleanStaleOutputRejectsTraversal(t *testing.T) {
419+
dir := t.TempDir()
420+
parentFile := filepath.Join(dir, "parent-file")
421+
os.WriteFile(parentFile, []byte("keep"), 0o600)
422+
423+
childDir := filepath.Join(dir, "child")
424+
os.MkdirAll(childDir, 0o755)
425+
426+
params := map[string]any{
427+
"interpretedOutput": "../parent-file",
428+
}
429+
430+
cleanStaleOutput("codeql_query_run", params, childDir)
431+
432+
if !fileExists(parentFile) {
433+
t.Error("traversal path should NOT be removed")
434+
}
435+
}
436+
437+
func TestCleanStaleOutputSkipsNonQueryRun(t *testing.T) {
438+
dir := t.TempDir()
439+
staleFile := filepath.Join(dir, "output.txt")
440+
os.WriteFile(staleFile, []byte("keep"), 0o600)
441+
442+
params := map[string]any{
443+
"interpretedOutput": "output.txt",
444+
}
445+
446+
cleanStaleOutput("codeql_test_run", params, dir)
447+
448+
if !fileExists(staleFile) {
449+
t.Error("non-codeql_query_run tool should not trigger cleanup")
450+
}
451+
}
452+
453+
func TestCleanStaleOutputSkipsWhenNoParam(t *testing.T) {
454+
dir := t.TempDir()
455+
params := map[string]any{
456+
"query": "example.ql",
457+
"database": "/some/db",
458+
}
459+
460+
// Should not panic or error — just no-op
461+
cleanStaleOutput("codeql_query_run", params, dir)
462+
}

0 commit comments

Comments
 (0)