From 9a91bb3072fe7aca4174b82af3eaef0d72c9a681 Mon Sep 17 00:00:00 2001 From: Raphael Fakhri <153192858+RaphaelFakhri@users.noreply.github.com> Date: Tue, 3 Nov 2026 06:46:30 +0000 Subject: [PATCH] validate: fix panic on missing host-specific hook With --host-specific, checkEventHooks reports a hook path that cannot be stat'ed, but then calls Mode() on the nil FileInfo anyway, so `oci-runtime-tool --host-specific validate` panics instead of printing the "cannot find hook" error. Only check the executable bit when the stat succeeds. Signed-off-by: Raphael Fakhri <153192858+RaphaelFakhri@users.noreply.github.com> --- validate/validate.go | 3 +-- validate/validate_test.go | 42 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/validate/validate.go b/validate/validate.go index 4b2de5dc..24b3729f 100644 --- a/validate/validate.go +++ b/validate/validate.go @@ -298,8 +298,7 @@ func (v *Validator) checkEventHooks(hookType string, hooks []rspec.Hook, hostSpe fi, err := os.Stat(hook.Path) if err != nil { errs = multierror.Append(errs, fmt.Errorf("cannot find %s hook: %v", hookType, hook.Path)) - } - if fi.Mode()&0o111 == 0 { + } else if fi.Mode()&0o111 == 0 { errs = multierror.Append(errs, fmt.Errorf("the %s hook %v: is not executable", hookType, hook.Path)) } } diff --git a/validate/validate_test.go b/validate/validate_test.go index 991dbbc3..8c6d2d77 100644 --- a/validate/validate_test.go +++ b/validate/validate_test.go @@ -702,6 +702,48 @@ func TestCheckHooks(t *testing.T) { } } +func TestCheckHooksHostSpecific(t *testing.T) { + dir := t.TempDir() + executable := filepath.Join(dir, "executable") + if err := os.WriteFile(executable, nil, 0o755); err != nil { + t.Fatal(err) + } + notExecutable := filepath.Join(dir, "not-executable") + if err := os.WriteFile(notExecutable, nil, 0o644); err != nil { + t.Fatal(err) + } + missing := filepath.Join(dir, "missing") + + cases := []struct { + path string + expected string + }{ + {path: executable, expected: ""}, + {path: notExecutable, expected: "is not executable"}, + {path: missing, expected: "cannot find prestart hook"}, + } + for _, c := range cases { + spec := rspec.Spec{ + Version: "1.0.0", + Hooks: &rspec.Hooks{ + Prestart: []rspec.Hook{{Path: c.path}}, //nolint:staticcheck // Ignore SA1019: rspec.Hooks.Prestart is deprecated + }, + } + v, err := NewValidator(&spec, ".", true, "linux") + if err != nil { + t.Fatalf("unexpected NewValidator error: %+v", err) + } + err = multierror.Append(nil, v.CheckHooks()).ErrorOrNil() + if c.expected == "" { + assert.NoError(t, err, c.path) + continue + } + if assert.Error(t, err, c.path) { + assert.Contains(t, err.Error(), c.expected, c.path) + } + } +} + func TestCheckMandatoryFields(t *testing.T) { for _, tt := range []struct { config *rspec.Spec