Files
Dominik Schulz 7c63ba09b7 chore(deps): migrate from urfave/cli v2 to v3 (#3428)
Migrate the entire codebase from github.com/urfave/cli/v2 to
github.com/urfave/cli/v3 (v3.9.0).

Key breaking changes addressed:
- cli.App removed, replaced by *cli.Command
- ActionFunc signature: func(*Context) error -> func(context.Context, *Command) error
- BeforeFunc signature: func(*Context) error -> func(context.Context, *Command) (context.Context, error)
- app.RunContext -> app.Run
- app.EnableBashCompletion -> app.EnableShellCompletion
- Subcommands field renamed to Commands
- EnvVars on flags -> Sources: cli.EnvVars(...)
- cli.NewContext removed; test helpers updated to use cmd.Run() pattern
- cli.Flag interface updated (Apply removed, Get/PreParse/PostParse/Set added)
- VersionPrinter type changed to func(*Command)

Also updates .capabilities.json baseline to reflect new cli/v3 call paths.

Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>
2026-05-16 18:13:19 +02:00

74 lines
2.1 KiB
Go

package action
import (
"context"
"fmt"
"os"
"strings"
"github.com/gopasspw/gopass/internal/action/exit"
"github.com/gopasspw/gopass/internal/out"
"github.com/gopasspw/gopass/internal/tpl"
"github.com/gopasspw/gopass/pkg/ctxutil"
"github.com/gopasspw/gopass/pkg/gopass"
"github.com/urfave/cli/v3"
)
// secretGetter is the minimal store interface required by the template engine.
type secretGetter interface {
Get(context.Context, string) (gopass.Secret, error)
}
// pathRestrictedStore wraps a secretGetter and only allows Get() calls for
// secret names that have one of the configured path prefixes. All other
// accesses are denied with a generic error so untrusted templates cannot
// exfiltrate secrets outside the declared scope.
type pathRestrictedStore struct {
inner secretGetter
allowPaths []string
}
func (p *pathRestrictedStore) Get(ctx context.Context, name string) (gopass.Secret, error) {
for _, prefix := range p.allowPaths {
if strings.HasPrefix(name, prefix) {
return p.inner.Get(ctx, name)
}
}
return nil, fmt.Errorf("access denied: %q is not within an allowed path", name)
}
// Process is a command to process a template and replace secrets contained in it.
func (s *miscHandler) Process(ctx context.Context, cmd *cli.Command) error {
ctx = ctxutil.WithGlobalFlags(ctx, cmd)
file := cmd.Args().First()
if file == "" {
return exit.Error(exit.Usage, nil, "Usage: %s process <FILE>", s.Name)
}
allowPaths := cmd.StringSlice("allow-path")
buf, err := os.ReadFile(file)
if err != nil {
return exit.Error(exit.IO, err, "Failed to read file: %s", file)
}
// Decide which store view the template engine may access.
var store secretGetter
if len(allowPaths) > 0 {
store = &pathRestrictedStore{inner: s.Store, allowPaths: allowPaths}
} else {
out.Warningf(ctx, "No --allow-path flag set. The template has unrestricted access to ALL secrets in the store. Only process templates from trusted sources.")
store = s.Store
}
obuf, err := tpl.Execute(ctx, string(buf), file, nil, store)
if err != nil {
return exit.Error(exit.IO, err, "Failed to process file: %s", file)
}
out.Print(ctx, string(obuf))
return nil
}