Search Apps Documentation Source Content File Folder Download Copy Actions Download

nestedpkg.gno

3.36 Kb · 107 lines
  1// Package nestedpkg provides helpers for package-path based access control.
  2// It is useful for upgrade patterns relying on namespaces.
  3//
  4// SECURITY: every exported helper takes `rlm realm` and reads both
  5// `rlm.PkgPath()` and `rlm.Previous().PkgPath()` to make an
  6// authorization decision. To close Class-2 designation forgery (a
  7// hostile realm stashes a captured realm value and passes it back to
  8// spoof identity), every helper gates on `rlm.IsCurrent()` first. The
  9// Is* predicates return false on stale rlm (fail-closed); the Assert*
 10// helpers panic. See docs/resources/gno-security.md.
 11package nestedpkg
 12
 13import "strings"
 14
 15// IsCallerSubPath checks if the caller realm is located in a subfolder of the current realm.
 16func IsCallerSubPath(_ int, rlm realm) bool {
 17	if !rlm.IsCurrent() {
 18		return false
 19	}
 20	var (
 21		curPath  = rlm.PkgPath() + "/"
 22		prevPath = rlm.Previous().PkgPath() + "/"
 23	)
 24	return strings.HasPrefix(prevPath, curPath)
 25}
 26
 27// AssertCallerIsSubPath panics if IsCallerSubPath returns false.
 28func AssertCallerIsSubPath(_ int, rlm realm) {
 29	if !rlm.IsCurrent() {
 30		panic("unauthorized: rlm is not the caller's live cur")
 31	}
 32	var (
 33		curPath  = rlm.PkgPath() + "/"
 34		prevPath = rlm.Previous().PkgPath() + "/"
 35	)
 36	if !strings.HasPrefix(prevPath, curPath) {
 37		panic("call restricted to nested packages. current realm is " + curPath + ", previous realm is " + prevPath)
 38	}
 39}
 40
 41// IsCallerParentPath checks if the caller realm is located in a parent location of the current realm.
 42func IsCallerParentPath(_ int, rlm realm) bool {
 43	if !rlm.IsCurrent() {
 44		return false
 45	}
 46	var (
 47		curPath  = rlm.PkgPath() + "/"
 48		prevPath = rlm.Previous().PkgPath() + "/"
 49	)
 50	return strings.HasPrefix(curPath, prevPath)
 51}
 52
 53// AssertCallerIsParentPath panics if IsCallerParentPath returns false.
 54func AssertCallerIsParentPath(_ int, rlm realm) {
 55	if !rlm.IsCurrent() {
 56		panic("unauthorized: rlm is not the caller's live cur")
 57	}
 58	var (
 59		curPath  = rlm.PkgPath() + "/"
 60		prevPath = rlm.Previous().PkgPath() + "/"
 61	)
 62	if !strings.HasPrefix(curPath, prevPath) {
 63		panic("call restricted to parent packages. current realm is " + curPath + ", previous realm is " + prevPath)
 64	}
 65}
 66
 67// IsSameNamespace checks if the caller realm and the current realm are in the same namespace.
 68func IsSameNamespace(_ int, rlm realm) bool {
 69	if !rlm.IsCurrent() {
 70		return false
 71	}
 72	var (
 73		curNs  = nsFromPath(rlm.PkgPath()) + "/"
 74		prevNs = nsFromPath(rlm.Previous().PkgPath()) + "/"
 75	)
 76	return curNs == prevNs
 77}
 78
 79// AssertIsSameNamespace panics if IsSameNamespace returns false.
 80func AssertIsSameNamespace(_ int, rlm realm) {
 81	if !rlm.IsCurrent() {
 82		panic("unauthorized: rlm is not the caller's live cur")
 83	}
 84	var (
 85		curNs  = nsFromPath(rlm.PkgPath()) + "/"
 86		prevNs = nsFromPath(rlm.Previous().PkgPath()) + "/"
 87	)
 88	if curNs != prevNs {
 89		panic("call restricted to packages from the same namespace. current realm is " + curNs + ", previous realm is " + prevNs)
 90	}
 91}
 92
 93// nsFromPath extracts the namespace from a package path.
 94func nsFromPath(pkgpath string) string {
 95	parts := strings.Split(pkgpath, "/")
 96
 97	// Specifically for gno.land, potential paths are in the form of DOMAIN/r/NAMESPACE/...
 98	// XXX: Consider extra checks.
 99	// XXX: Support non gno.land domains, where p/ and r/ won't be enforced.
100	if len(parts) >= 3 {
101		return parts[2]
102	}
103	return ""
104}
105
106// XXX: Consider adding IsCallerDirectlySubPath
107// XXX: Consider adding IsCallerDirectlyParentPath