PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/gnops/valopers

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
valopers
Namespace
gnops
Files
14 (gnomod.toml)
Exported functions
13
Module
gno.land/r/gnops/valopers
gno
0.9

Files (14)

  • gnomod.tomltoml
  • admin.gnogno
  • init.gnogno
  • valopers.gnogno
  • admin_test.gnogno
  • pubkey_type_test.gnogno
  • rotate_test.gnogno
  • valopers_test.gnogno
  • z_1_filetest.gnogno
  • z_2_filetest.gnogno
  • z_auth_readonly_filetest.gnogno
  • z_foreign_realm_capability_filetest.gnogno
  • z_govdao_only_principal_filetest.gnogno
  • z_operator_authlist_capability_filetest.gnogno
  • z_operator_authlist_capability_filetest.gnogno
    1// PKGPATH: gno.land/r/zzoperatorauth2//3// Regression pin for the per-operator auth-list capability leak4// (reported by @thehowl; pre-existing, fixed here because it is the same5// class as the governance capability leak, and the same audit that found6// that one should have found it).7//

    Functions

    • AddToAuthList(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, member string)

    • Auth() string

    • DeleteFromAuthList(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, member string)

    • GetByAddr(addr string) struct{Moniker string; Description string; ServerType string; OperatorAddress .uverse.address; SigningPubKey string; SigningAddress .uverse.address; LastRotationHeight int64; KeepRunning bool; auth *gno.land/p/nt/ownable/exts/authorizable/v0.Authorizable}

    • NewAuthorityRotationProposalRequest(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, newContractPath string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • NewInstructionsProposalRequest(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, newInstructions string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • Register(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, moniker string, description string, serverType string, addr string, pubKey string)

    • Render(fullPath string) string

    • UpdateDescription(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, description string)

    • UpdateKeepRunning(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, keepRunning bool)

    • UpdateMoniker(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, moniker string)

    • UpdateServerType(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, serverType string)

    • UpdateSigningKey(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, newPubKey string)

    Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

    Rendered

    RenderedRawgnoweb ↗
    8// THE HOLE. `Valoper.Auth() *authorizable.Authorizable` was exported and
    9// `GetByAddr` is exported and non-crossing. Valoper is returned BY
    10// VALUE, but `auth` is a pointer field, so the copy shared the realm's
    11// live Authorizable. Authorizable's gates read
    12// `rlm.Previous().Address()`, so inside a hostile realm's frame
    13// Previous() is whoever called it. A valoper OPERATOR merely calling any
    14// function of a hostile realm (faucet, airdrop, mint) let that realm run
    15//
    16// valopers.GetByAddr(operator).Auth().AddToAuthList(0, cur, attacker)
    17//
    18// with Previous() == the operator, i.e. the Authorizable's own owner.
    19// The write was accepted and PERSISTED. From the next transaction on,
    20// the attacker acted alone: UpdateKeepRunning to drain the validator,
    21// UpdateSigningKey to rotate its consensus signing key.
    22//
    23// THE FIX is the one layer 2 applies to the realm's governance
    24// authority: never export the live capability. `Auth()` is removed;
    25// `AuthOwner()` copies out an address.
    26//
    27// WHAT THIS FILE PINS, honestly scoped. The raw-handle attack is a
    28// compile-time absence now, so — like the foreign-realm filetest —
    29// reverting the fix fails this on a build error rather than an
    30// assertion. What it
    31// drives at RUNTIME is the property that makes the removal safe: the
    32// exported wrapper is NOT an equivalent route. There `cur.Previous()` is
    33// the hostile realm rather than the operator, so the superuser check
    34// refuses, and it refuses even in the exact scenario that used to work —
    35// the operator calling the hostile realm themselves.
    36//
    37// NOTE for whoever edits valopers next: re-adding any exported accessor
    38// that returns *authorizable.Authorizable (or the *ownable.Ownable
    39// inside it) reopens this, and this file would still compile.
    40package zzoperatorauth
    41
    42import (
    43 "chain"
    44 "testing"
    45
    46 "gno.land/p/nt/testutils/v0"
    47 "gno.land/r/gnops/valopers"
    48)
    49
    50var (
    51 operator = testutils.TestAddress("operator")
    52 attacker = chain.PackageAddress("gno.land/r/zzoperatorauth")
    53 pubKey = "gpub1pggj7ard9eg82cjtv4u52epjx56nzwgjyg9zqwpdwpd0f9fvqla089ndw5g9hcsufad77fml2vlu73fk8q8sh8v72cza5p"
    54)
    55
    56// Pwn is an ordinary exported function of a hostile realm — the airdrop
    57// the operator was told to claim. It needs no privilege of its own; it
    58// used to just read the public handle and write through it.
    59func Pwn(cur realm, victim address) {
    60 // The attack the fix removes. Uncommenting must not compile:
    61 //
    62 // valopers.GetByAddr(victim).Auth().AddToAuthList(0, cur, attacker)
    63 // ^^^^^^ undefined
    64 //
    65 // What remains is the exported wrapper. It is not equivalent: it
    66 // derives the principal from THIS frame, where Previous() is
    67 // gno.land/r/zzoperatorauth and not the operator. Aborts.
    68 valopers.AddToAuthList(cross(cur), victim, attacker)
    69 println("UNREACHABLE: hostile realm joined the operator's auth list")
    70}
    71
    72func main(cur realm) {
    73 // The operator registers their profile normally.
    74 testing.SetOriginCaller(operator)
    75 testing.SetRealm(testing.NewUserRealm(operator))
    76 valopers.Register(cross(cur), "Op",
    77 "a valid description for the operator profile", "cloud", operator, pubKey)
    78
    79 // The auth list is owned by the operator, and AuthOwner hands back
    80 // an address — a value, not a capability.
    81 println("owner is operator:",
    82 valopers.GetByAddr(operator).AuthOwner() == operator)
    83 println("attacker is not owner:",
    84 valopers.GetByAddr(operator).AuthOwner() != attacker)
    85
    86 // The operator calls the hostile realm once. This single step used
    87 // to be the whole attack.
    88 testing.SetOriginCaller(operator)
    89 testing.SetRealm(testing.NewUserRealm(operator))
    90 Pwn(cross(cur), operator)
    91}
    92
    93// Output:
    94// owner is operator: true
    95// attacker is not owner: true
    96
    97// Error:
    98// authorizable: caller is not superuser
    99

    vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.