diff --git a/gno.land/cmd/gnoland/testdata/addpkg_namespace.txtar b/gno.land/cmd/gnoland/testdata/addpkg_namespace.txtar new file mode 100644 index 00000000000..959237cc910 --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/addpkg_namespace.txtar @@ -0,0 +1,33 @@ +loadpkg gno.land/r/demo/users + +adduser gui + +gnoland start + +! gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/guil/counter -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test test1 +stderr 'unauthorized user' + +gnokey maketx call -pkgpath gno.land/r/demo/users -func Invite -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test -args $USER_ADDR_gui test1 + +gnokey maketx call -pkgpath gno.land/r/demo/users -func Register -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test -args $USER_ADDR_test1 -args 'guiland' -args 'im gui' gui + +gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/guiland/counter -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test gui + +-- counter.gno -- +package counter + +import "strconv" + +var count = 0 + +func Inc() { + count += 1 +} + +func Render(path string) string { + return "# Counter: " + strconv.Itoa(count) +} + + +-- gno.mod -- +module gno.land/r/guiland/counter diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index a77ddac3e28..c73f6518052 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -7,10 +7,13 @@ import ( "context" "fmt" "os" + "path/filepath" "strings" + "github.com/gnolang/gno/gnovm/pkg/gnolang" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" "github.com/gnolang/gno/gnovm/stdlibs" + "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/errors" "github.com/gnolang/gno/tm2/pkg/sdk" "github.com/gnolang/gno/tm2/pkg/sdk/auth" @@ -23,6 +26,11 @@ import ( "go.opentelemetry.io/otel/metric" ) +var ( + ErrInvalidUrl = errors.New("invalid url") + ErrUnauthorizedUser = errors.New("unauthorized user") +) + const ( maxAllocTx = 500 * 1000 * 1000 maxAllocQuery = 1500 * 1000 * 1000 // higher limit for queries @@ -135,6 +143,69 @@ func (vm *VMKeeper) getGnoStore(ctx sdk.Context) gno.Store { } } +func (vm *VMKeeper) checkNamespacePerm(ctx sdk.Context, creator crypto.Address, pkgPath string) error { + const sysUsersPkg = "gno.land/r/demo/users" + + store := vm.getGnoStore(ctx) + + // if r/sys/names does not exists -> skip validation. + usersPkg := store.GetPackage(sysUsersPkg, false) + if usersPkg == nil { + return nil + } + + pkgPath = filepath.Clean(pkgPath) // cleanup pkgpath + pathSp := strings.SplitN(pkgPath, "/", 4) // gno.land/r// + if len(pathSp) < 3 { + return fmt.Errorf("%w: %s", ErrInvalidUrl, pkgPath) + } + + // XXX: cleanup username + username := pathSp[2] + + // Parse and run the files, construct *PV. + pkgAddr := gno.DerivePkgAddr(pkgPath) + msgCtx := stdlibs.ExecContext{ + ChainID: ctx.ChainID(), + Height: ctx.BlockHeight(), + Timestamp: ctx.BlockTime().Unix(), + OrigCaller: creator.Bech32(), + OrigSendSpent: new(std.Coins), + OrigPkgAddr: pkgAddr.Bech32(), + Banker: NewSDKBanker(vm, ctx), + EventLogger: ctx.EventLogger(), + } + + m := gno.NewMachineWithOptions( + gno.MachineOptions{ + PkgPath: pkgPath, + Output: os.Stdout, // XXX + Store: store, + Context: msgCtx, + Alloc: store.GetAllocator(), + MaxCycles: vm.maxCycles, + GasMeter: ctx.GasMeter(), + }) + defer m.Release() + + m.RunDeclaration(gno.ImportD("users", sysUsersPkg)) + x := gno.Call( + gno.Sel(gnolang.Nx("users"), "GetUserByAddressOrName"), // Call testing.RunTest + gno.Str("@"+username), // First param, the name of the test + ) + + ret := m.Eval(x) + if len(ret) == 0 { + panic("invalid response length call") + } + + if user := ret[0]; user.V == nil { + return fmt.Errorf("%w: %s", ErrUnauthorizedUser, pkgPath) + } + + return nil +} + // AddPackage adds a package with given fileset. func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) { creator := msg.Creator @@ -177,6 +248,10 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) { // - check if caller is in Admins or Editors. // - check if namespace is not in pause. + if err := vm.checkNamespacePerm(ctx, creator, pkgPath); err != nil { + return err + } + err = vm.bank.SendCoins(ctx, creator, pkgAddr, deposit) if err != nil { return err