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/p/moul/gnopm/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
moul / gnopm
Files
10 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/moul/gnopm/v0
gno
0.9

Files (10)

  • README.mdmarkdown
  • gnomod.tomltoml
  • errors.gnogno
  • pkgpath.gnogno
  • registry.gnogno
  • validate.gnogno
  • helpers_test.gnogno
  • pkgpath_test.gnogno
  • registry_test.gnogno
  • validate_test.gnogno
  • validate.gnogno
    1package gnopm23import "strings"45// Size caps. Every string the chain stores is bounded: an unbounded field is an6// unbounded storage deposit, and on gno.land the deposit is paid per byte by7// whoever writes it.8

    Functions

    not supported for pure packages by the node (vm/qfuncs)

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

    const (
    9 MaxPkgPathLen = 256
    10 MaxRepoLen = 512
    11 MaxDirLen = 256
    12 MaxRefLen = 255 // git's own limit for a single ref name
    13
    14 // MaxClaimants bounds how many addresses may claim one package path.
    15 // Unbounded, it is a spam surface: one path with ten thousand claims is a
    16 // Render that never returns and a listing nobody can read.
    17 MaxClaimants = 16
    18)
    19
    20// ValidCommit reports whether s is a git object id: 40 (SHA-1) or 64 (SHA-256)
    21// lowercase hex characters. Case is fixed so the same object always produces
    22// the same stored bytes, and so a verifier comparing two claims compares two
    23// comparable strings.
    24func ValidCommit(s string) bool {
    25 if len(s) != 40 && len(s) != 64 {
    26 return false
    27 }
    28 for i := 0; i < len(s); i++ {
    29 c := s[i]
    30 if (c < '0' || c > '9') && (c < 'a' || c > 'f') {
    31 return false
    32 }
    33 }
    34 return true
    35}
    36
    37// ValidRepo reports whether s is a repository URL this registry accepts.
    38//
    39// Deliberately narrow: "https://" only, a host, and a path. Not a taste
    40// judgement about git transports, a safety one. Whatever is stored here is
    41// eventually rendered as a link by this realm, by gnoweb and by every explorer
    42// that reads the registry, and the set of schemes that are safe to hand a
    43// browser is exactly one. "javascript:", "data:" and "file:" are the attack;
    44// "git://" and "ssh://" are merely unreachable from a web page, and a claimant
    45// who needs one can point at the https mirror every forge already serves.
    46func ValidRepo(s string) bool {
    47 if s == "" || len(s) > MaxRepoLen {
    48 return false
    49 }
    50 const scheme = "https://"
    51 if !strings.HasPrefix(s, scheme) {
    52 return false
    53 }
    54 rest := s[len(scheme):]
    55 if rest == "" || strings.HasPrefix(rest, "/") {
    56 return false
    57 }
    58 // A host must be present and must not be a userinfo trick
    59 // ("https://github.com@evil.example/x" fetches from evil.example while
    60 // reading as GitHub).
    61 slash := strings.IndexByte(rest, '/')
    62 host := rest
    63 if slash >= 0 {
    64 host = rest[:slash]
    65 }
    66 if host == "" || strings.ContainsAny(host, "@:") {
    67 return false
    68 }
    69 if !strings.Contains(host, ".") {
    70 return false
    71 }
    72 for i := 0; i < len(s); i++ {
    73 if !urlByteOK(s[i]) {
    74 return false
    75 }
    76 }
    77 return !strings.Contains(s, "..")
    78}
    79
    80// urlByteOK is an allowlist, not a denylist: the printable ASCII that appears
    81// in a real repository URL. Everything else, control characters and the bidi
    82// overrides in particular, is refused rather than escaped, because a field that
    83// can only hold safe bytes needs no escaping at any of its render sites.
    84func urlByteOK(c byte) bool {
    85 switch {
    86 case c >= 'a' && c <= 'z', c >= 'A' && c <= 'Z', c >= '0' && c <= '9':
    87 return true
    88 }
    89 switch c {
    90 case '-', '.', '_', '~', ':', '/', '%', '+':
    91 return true
    92 }
    93 return false
    94}
    95
    96// ValidDir reports whether s names the subdirectory of the repository holding
    97// the package. Empty means the repository root, which is the common case for a
    98// single-package repo and must stay expressible.
    99func ValidDir(s string) bool {
    100 if s == "" {
    101 return true
    102 }
    103 if len(s) > MaxDirLen {
    104 return false
    105 }
    106 if strings.HasPrefix(s, "/") || strings.HasSuffix(s, "/") {
    107 return false
    108 }
    109 for _, p := range strings.Split(s, "/") {
    110 if p == "" || p == "." || p == ".." {
    111 return false
    112 }
    113 for i := 0; i < len(p); i++ {
    114 if !pathByteOK(p[i]) {
    115 return false
    116 }
    117 }
    118 }
    119 return true
    120}
    121
    122func pathByteOK(c byte) bool {
    123 switch {
    124 case c >= 'a' && c <= 'z', c >= 'A' && c <= 'Z', c >= '0' && c <= '9':
    125 return true
    126 }
    127 switch c {
    128 case '-', '.', '_':
    129 return true
    130 }
    131 return false
    132}
    133
    134// ValidRef reports whether s is a fully-qualified git ref, or empty.
    135//
    136// The ref is optional and is never the thing that is verified: a ref moves, a
    137// commit does not. It is recorded so a reader can tell a claim pinned to a
    138// released tag from one pinned to a commit on nobody's branch, and so a
    139// verifier can check the claimed commit is still reachable from it rather than
    140// dangling behind a force-push.
    141func ValidRef(s string) bool {
    142 if s == "" {
    143 return true
    144 }
    145 if len(s) > MaxRefLen || !strings.HasPrefix(s, "refs/") {
    146 return false
    147 }
    148 if strings.Contains(s, "..") || strings.Contains(s, "@{") {
    149 return false
    150 }
    151 if strings.HasSuffix(s, "/") || strings.HasSuffix(s, ".") {
    152 return false
    153 }
    154 parts := strings.Split(s, "/")
    155 if len(parts) < 2 {
    156 return false
    157 }
    158 for _, p := range parts {
    159 if p == "" || p == "@" || strings.HasPrefix(p, ".") || strings.HasSuffix(p, ".lock") {
    160 return false
    161 }
    162 for i := 0; i < len(p); i++ {
    163 if !refByteOK(p[i]) {
    164 return false
    165 }
    166 }
    167 }
    168 return true
    169}
    170
    171// refByteOK is an allowlist, and deliberately narrower than git's own rule.
    172//
    173// git rejects a handful of metacharacters and permits everything else, which
    174// leaves a ref free to contain a backtick, a pipe or a bracket. Every other
    175// field here is an allowlist precisely so that no consumer has to escape
    176// anything, and one denylist field would undo that for all of them: a ref
    177// carrying a pipe breaks out of a markdown table cell, and one carrying a
    178// backtick breaks out of the inline-code span a renderer wraps it in.
    179//
    180// The cost is refs nobody writes. What stays expressible is every ref anyone
    181// actually has: alphanumerics, and the four separators git tooling puts in a
    182// name.
    183func refByteOK(c byte) bool {
    184 switch {
    185 case c >= 'a' && c <= 'z', c >= 'A' && c <= 'Z', c >= '0' && c <= '9':
    186 return true
    187 }
    188 switch c {
    189 case '-', '.', '_', '/', '+':
    190 return true
    191 }
    192 return false
    193}
    194