From 2c4da261ea24dc7087c5733f03f314410d0a2b23 Mon Sep 17 00:00:00 2001 From: Pavel Rachevskiy Date: Thu, 4 Jun 2026 17:35:01 +0200 Subject: [PATCH 1/3] Fix permission denied for external config --- internal/config/config.go | 2 +- internal/config/config_test.go | 40 ++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/internal/config/config.go b/internal/config/config.go index 59c7575..649b04a 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -126,7 +126,7 @@ func parseExternalServiceConfig(path string) (ServiceConfig, error) { var cfg ServiceConfig extData, err := os.ReadFile(path) if err != nil { - if os.IsNotExist(err) { + if os.IsNotExist(err) || os.IsPermission(err) { return cfg, nil } return cfg, fmt.Errorf("read service config %q: %w", path, err) diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 5fcb579..9ee77a0 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -135,6 +135,46 @@ services: } } +func TestLoad_ServiceExternalPermissionDenied(t *testing.T) { + tmpDir := t.TempDir() + extFile := filepath.Join(tmpDir, "restricted.yaml") + if err := os.WriteFile(extFile, []byte(` +runtimes: + java: + version: "11" +`), 0o644); err != nil { + t.Fatalf("write restricted external config: %v", err) + } + if err := os.Chmod(extFile, 0); err != nil { + t.Fatalf("restrict external config permissions: %v", err) + } + defer func() { + if err := os.Chmod(extFile, 0o644); err != nil { + t.Logf("restore external config permissions: %v", err) + } + }() + if _, err := os.ReadFile(extFile); err == nil { + t.Skip("external config remains readable after permission restriction") + } else if !os.IsPermission(err) { + t.Fatalf("expected permission error reading external config, got: %v", err) + } + + mainContent := []byte(` +services: + foo: + path: "` + extFile + `" +`) + mainFile := filepath.Join(tmpDir, "cfg.yaml") + if err := os.WriteFile(mainFile, mainContent, 0o644); err != nil { + t.Fatalf("write main config: %v", err) + } + + _, err := Load(mainFile) + if err != nil { + t.Fatalf("Load() returned error for permission denied external service config: %v", err) + } +} + func TestLoad_ServiceExternalParseError(t *testing.T) { tmpDir := t.TempDir() extFile := filepath.Join(tmpDir, "bad.yaml") From b3795782f0e863dad5a1dd019eb20b8554d7ab60 Mon Sep 17 00:00:00 2001 From: Pavel Rachevskiy Date: Thu, 4 Jun 2026 17:44:06 +0200 Subject: [PATCH 2/3] fix linters --- cmd/ad-runtime-utils/app.go | 10 ++++++---- internal/detect/resolve.go | 13 +++++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/cmd/ad-runtime-utils/app.go b/cmd/ad-runtime-utils/app.go index f3dbae6..a6e19a6 100644 --- a/cmd/ad-runtime-utils/app.go +++ b/cmd/ad-runtime-utils/app.go @@ -18,6 +18,8 @@ const ( exitOK = 0 exitUserError = 1 exitParseError = 2 + runtimeJava = "java" + runtimePython = "python" ) func Run(args []string, stdout, stderr io.Writer) int { @@ -54,12 +56,12 @@ func Run(args []string, stdout, stderr io.Writer) int { } if *printCACerts { - if strings.ToLower(*runtime) != "java" { + if strings.ToLower(*runtime) != runtimeJava { fmt.Fprintln(stderr, "--print-cacerts is only valid with --runtime=java") return exitUserError } var javaHome string - javaHome, err = detect.ResolveRuntime(cfg, *service, "java") + javaHome, err = detect.ResolveRuntime(cfg, *service, runtimeJava) if err != nil { fmt.Fprintf(stderr, "detection failed: %v\n", err) return exitUserError @@ -106,9 +108,9 @@ func detectEnvName(cfg *config.Config, service, runtime string) string { return def.EnvVar } switch strings.ToLower(runtime) { - case "java": + case runtimeJava: return "JAVA_HOME" - case "python": + case runtimePython: return "VIRTUAL_ENV" default: return strings.ToUpper(runtime) + "_HOME" diff --git a/internal/detect/resolve.go b/internal/detect/resolve.go index 59bffd0..fc9a086 100644 --- a/internal/detect/resolve.go +++ b/internal/detect/resolve.go @@ -6,12 +6,17 @@ import ( "github.com/arenadata/ad-runtime-utils/internal/config" ) +const ( + runtimeJava = "java" + runtimePython = "python" +) + func exeName(rt string) string { switch rt { - case "java": - return "java" - case "python": - return "python" + case runtimeJava: + return runtimeJava + case runtimePython: + return runtimePython default: return rt } From 76d8ff1e4430d1f2e7232e23ab7a36a5846031ea Mon Sep 17 00:00:00 2001 From: Pavel Rachevskiy Date: Thu, 4 Jun 2026 17:52:05 +0200 Subject: [PATCH 3/3] fix linters --- internal/detect/detect.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/detect/detect.go b/internal/detect/detect.go index b44c849..62666bf 100644 --- a/internal/detect/detect.go +++ b/internal/detect/detect.go @@ -19,6 +19,7 @@ func checkCandidate(cand, exe string) (string, bool) { resolved = cand } candidateExe := filepath.Join(resolved, "bin", exe) + // #nosec G703 -- Runtime homes intentionally come from config/env; this only probes bin/exe existence. if _, statErr := os.Stat(candidateExe); statErr == nil { return resolved, true } @@ -62,7 +63,7 @@ func tryEnvVar(cfg config.RuntimeSetting, exe string) (string, bool) { } p := expandPath(raw) - if _, err := os.Stat(filepath.Join(p, "bin", exe)); err == nil { + if _, ok := checkCandidate(p, exe); ok { return p, true } return "", false