Reject special files before preparing inputs
This commit is contained in:
@@ -276,8 +276,8 @@ func requireInspectionFile(path, label string) error {
|
|||||||
}
|
}
|
||||||
return fmt.Errorf("stat %s %q: %w", label, path, err)
|
return fmt.Errorf("stat %s %q: %w", label, path, err)
|
||||||
}
|
}
|
||||||
if info.IsDir() {
|
if !info.Mode().IsRegular() {
|
||||||
return fmt.Errorf("%s %q is a directory", label, path)
|
return fmt.Errorf("%s %q is not a regular file", label, path)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
34
internal/app/operator_inspection_fifo_test.go
Normal file
34
internal/app/operator_inspection_fifo_test.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//go:build unix
|
||||||
|
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInspectStableInputsRejectsSpellCatalogFIFO(t *testing.T) {
|
||||||
|
root := t.TempDir()
|
||||||
|
sourcePath := filepath.Join(root, "spells.fifo")
|
||||||
|
if err := unix.Mkfifo(sourcePath, 0o644); err != nil {
|
||||||
|
t.Fatalf("Mkfifo() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config.Config{StableInputs: config.ResolvedStableInputs{
|
||||||
|
SpellCatalogFile: config.ResolvedInputFile{Path: sourcePath, ConfigPath: filepath.Join(root, "campaign.yml")},
|
||||||
|
}}
|
||||||
|
for _, check := range inspectStableInputs(cfg) {
|
||||||
|
if check.Name != "spell_catalog" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if check.Err == nil || !strings.Contains(check.Err.Error(), "not a regular file") {
|
||||||
|
t.Fatalf("spell catalog check = %#v, want regular-file rejection", check)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Fatal("spell catalog inspection result was not reported")
|
||||||
|
}
|
||||||
@@ -635,6 +635,13 @@ func requireRegularReadableFile(path string, label string) error {
|
|||||||
if strings.TrimSpace(path) == "" {
|
if strings.TrimSpace(path) == "" {
|
||||||
return fmt.Errorf("%s path is required", label)
|
return fmt.Errorf("%s path is required", label)
|
||||||
}
|
}
|
||||||
|
declared, err := os.Lstat(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("inspect %s %q: %w", label, path, err)
|
||||||
|
}
|
||||||
|
if declared.Mode()&os.ModeSymlink != 0 || !declared.Mode().IsRegular() {
|
||||||
|
return fmt.Errorf("%s %q is not a regular file without symlinks", label, path)
|
||||||
|
}
|
||||||
file, err := os.Open(path)
|
file, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("open %s %q: %w", label, path, err)
|
return fmt.Errorf("open %s %q: %w", label, path, err)
|
||||||
@@ -644,8 +651,13 @@ func requireRegularReadableFile(path string, label string) error {
|
|||||||
if statErr != nil {
|
if statErr != nil {
|
||||||
return fmt.Errorf("stat %s %q: %w", label, path, statErr)
|
return fmt.Errorf("stat %s %q: %w", label, path, statErr)
|
||||||
}
|
}
|
||||||
if !info.Mode().IsRegular() {
|
current, currentErr := os.Lstat(path)
|
||||||
return fmt.Errorf("%s %q is not a regular file", label, path)
|
if currentErr != nil {
|
||||||
|
return fmt.Errorf("reinspect %s %q: %w", label, path, currentErr)
|
||||||
|
}
|
||||||
|
if !info.Mode().IsRegular() || current.Mode()&os.ModeSymlink != 0 || !current.Mode().IsRegular() ||
|
||||||
|
!os.SameFile(info, declared) || !os.SameFile(info, current) {
|
||||||
|
return fmt.Errorf("%s %q changed while being opened", label, path)
|
||||||
}
|
}
|
||||||
if closeErr != nil {
|
if closeErr != nil {
|
||||||
return fmt.Errorf("close %s %q: %w", label, path, closeErr)
|
return fmt.Errorf("close %s %q: %w", label, path, closeErr)
|
||||||
|
|||||||
45
internal/stage/prepare_fifo_test.go
Normal file
45
internal/stage/prepare_fifo_test.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
//go:build unix
|
||||||
|
|
||||||
|
package stage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPrepareStageRejectsSpellCatalogFIFOWithoutBlocking(t *testing.T) {
|
||||||
|
env, m := setupPrepareEnv(t)
|
||||||
|
root := filepath.Dir(env.Config.SessionPath)
|
||||||
|
writeFile(t, filepath.Join(root, "audio", "a.flac"), "audio")
|
||||||
|
env.Config.Session.Inputs.AudioFiles = []string{"./audio/a.flac"}
|
||||||
|
|
||||||
|
sourcePath := filepath.Join(root, "spells.fifo")
|
||||||
|
if err := unix.Mkfifo(sourcePath, 0o644); err != nil {
|
||||||
|
t.Fatalf("Mkfifo() error = %v", err)
|
||||||
|
}
|
||||||
|
env.Config.StableInputs.SpellCatalogFile = config.ResolvedInputFile{
|
||||||
|
Path: "./spells.fifo",
|
||||||
|
ConfigPath: env.Config.CampaignPath,
|
||||||
|
Source: "campaign_config",
|
||||||
|
}
|
||||||
|
|
||||||
|
done := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
_, err := (prepareStage{}).Run(context.Background(), env, m)
|
||||||
|
done <- err
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case err := <-done:
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "not a regular file") {
|
||||||
|
t.Fatalf("prepare.Run() error = %v, want regular-file rejection", err)
|
||||||
|
}
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
t.Fatal("prepare.Run() blocked while inspecting a FIFO spell catalog")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user