Documentation
¶
Overview ¶
Package rules holds the pure rules that a publisher's OVDB manifest is held to: what a published URL, a homepage, a database id, a commit, a repository, a path inside a repository, an engine name and a licence id may look like, and how two claimed addresses compare.
The rules are the OVDB Directory's (github.com/openvaultdb/directory, scripts/lib/urls.mjs, git.mjs and directory.mjs, CC0-1.0) as the Chinook database's own pre-check ports them (github.com/datatug/chinookdb, scripts/lib/directory-rules.mjs, MIT). Both are JavaScript. The rule of this package is that a function here never accepts a string either of them refuses; it may refuse more, and every way it does is recorded in README.md and proved by the reference matrix (see the tests).
Every function is pure: no file, no network, no clock, no YAML. Every function bounds its input first (see the Max constants) and then reads it once, left to right. The URL rules are written on the text, as a reader of a plain subset, and never hand the string to net/url: Go's parser and the WHATWG parser that the JavaScript relies on disagree on hosts whose last label is numeric, on punycode, on backslashes and on control characters.
Two traps for the code that uses this package. A text field is "required" when it is not blank by JavaScript's trim(), which is not Go's strings.TrimSpace (it strips U+FEFF and not U+0085, Go the reverse): use IsBlank, never strings.TrimSpace, for those checks. And claims conflict when Relation.Conflicts says so; never compare a Relation with Same or Under by hand, because an address that cannot be compared is a conflict.
Limits on input are part of the rules: an input longer than the bound of its function is refused (or, for Compare, reported as Incomparable) without being read further.
Index ¶
- Constants
- func ClaimedForm(address string) (string, bool)
- func CompareKey(s string) (string, bool)
- func Homepage(s string) error
- func IsBlank(s string) bool
- func IsCommit(s string) bool
- func IsEngine(s string) bool
- func IsID(s string) bool
- func IsLicenceID(s string) bool
- func IsPublishEntry(s string) bool
- func IsRepositoryPath(s string) bool
- func PublicHTTPSURL(s string) error
- func PublicHTTPSURLTemplate(s string) error
- func Quote(s string) string
- func RepositoryKey(s string) (string, bool)
- type Problem
- type Relation
- type Rule
- type URL
Constants ¶
const ( // MaxURLLength bounds every URL. The references have no bound on a URL. MaxURLLength = 2048 // MaxHostLength is the longest host of a URL, dots included. MaxHostLength = 253 // MaxLabelLength is the longest label of a host. MaxLabelLength = 63 // MaxHomepageLength is the longest homepage. MaxHomepageLength = 200 // MaxIDLength is the longest database id. MaxIDLength = 80 // MaxEngineLength is the longest engine name. MaxEngineLength = 40 // MaxLicenceLength is the longest licence id. MaxLicenceLength = 64 // MaxPathLength bounds a path inside a repository. The references have no // bound on one. MaxPathLength = 1024 // MaxRepositoryLength bounds a repository URL. The references have no bound // on one. MaxRepositoryLength = 255 // MaxClaimLength bounds an address that is compared with another. MaxClaimLength = MaxURLLength )
Bounds on input, in bytes. The bounds that the references state themselves (a host of at most 253 characters, a label of at most 63, an id of at most 80, a homepage of at most 200) are the references' rules; the others are this package's own and only ever refuse more.
Variables ¶
This section is empty.
Functions ¶
func ClaimedForm ¶
ClaimedForm is the form of an address that claims are compared in: ASCII lower case, trailing slashes removed. It returns false for an address that is longer than MaxClaimLength or not ASCII (the JavaScript reference folds case by Unicode rules, which this package does not copy, and the addresses that reach a comparison have already passed PublicHTTPSURL).
func CompareKey ¶
CompareKey is the key to compare two repositories by: RepositoryKey in lower case, since a host names one repository whatever the case it is written in. It returns false when s is not a repository.
func Homepage ¶
Homepage reports why s is not a manifest's homepage: a public https URL of at most 200 bytes.
func IsBlank ¶
IsBlank reports whether s is empty or only white space, the way JavaScript's String.prototype.trim() sees it: a manifest field is "required" when `typeof v === 'string' && v.trim() !== ”`, and the references refuse a blank one. White space there is the ECMAScript WhiteSpace and LineTerminator characters: tab, line feed, vertical tab, form feed, carriage return, space, U+00A0, U+1680, U+2000 to U+200A, U+2028, U+2029, U+202F, U+205F, U+3000 and the byte order mark U+FEFF. It is not Go's: strings.TrimSpace strips U+0085 (which JavaScript does not) and not U+FEFF (which JavaScript does), so a check of "is required" written with strings.TrimSpace would accept "\uFEFF" where both references refuse it. Use IsBlank for those checks.
It reads up to the first character that is not white space and stops, so it needs no bound of its own; a byte that is not valid UTF-8 is not white space.
func IsEngine ¶
IsEngine reports whether s is a deployment engine name: a letter, then up to 39 letters, digits and _ . + - (^[A-Za-z][A-Za-z0-9_.+-]{0,39}$).
func IsID ¶
IsID reports whether s is a database id: lower-case letters and digits in words joined by single hyphens, at most 80 bytes (^[a-z0-9]+(-[a-z0-9]+)*$).
func IsLicenceID ¶
IsLicenceID reports whether s has the shape of an SPDX licence id: a letter or digit, then up to 63 letters, digits and . + - (^[A-Za-z0-9][A-Za-z0-9.+-]{0,63}$). It does not know which ids SPDX has assigned.
func IsPublishEntry ¶
IsPublishEntry reports whether s is an entry of an OVDB.md publish list: an explicit path, ./ followed by a repository path (the explicit form is what says the publisher meant a file of this repository and not a pattern).
func IsRepositoryPath ¶
IsRepositoryPath reports whether s is a path to a file inside a repository, as a manifest names one: relative (no leading /), only A-Z a-z 0-9 . _ / and -, no //, no . or .. segment, no trailing /, at most 1024 bytes. A backslash, a glob character and a space are not in the set, so none passes.
func PublicHTTPSURL ¶
PublicHTTPSURL reports why s is not a public https URL, or nil.
A public https URL is written exactly like this: https://, a host, a path.
- https only; no userinfo, no port (not even :443), no query, no fragment, no percent escape, no whitespace, control character or backslash;
- the host is lower-case letters, digits and hyphens in dot-separated labels of 1 to 63 bytes (none starting or ending with a hyphen), at least two labels, at most 253 bytes, no trailing dot; no IP address, nothing a reader could take for one (a last label of digits, or 0x and hex digits), no single-label name, no local, internal or reserved suffix; an xn-- label only when it is the canonical punycode of Latin-1 letters;
- the path starts with / and holds only A-Z a-z 0-9 . _ ~ / and -, no //, no . or .. segment;
- the spelling is the canonical one: there is exactly one way to write a URL that passes.
It does not check that anything exists, and it cannot see the address a name resolves to: a public-looking name can resolve to a private address, which only the party that connects can check.
func PublicHTTPSURLTemplate ¶
PublicHTTPSURLTemplate is PublicHTTPSURL for a template: the literal {name} must appear exactly once, in the path.
func Quote ¶ added in v0.23.0
Quote is [show] for other packages: the one way a piece of input goes into a message, cut to a short length, quoted and escaped to printable ASCII.
func RepositoryKey ¶
RepositoryKey returns the canonical key of the repository that s names, and whether s is one: an https URL on an allow-listed host with exactly the host's number of path segments, each of A-Z a-z 0-9 . _ - and none of them . or .., the last not ending in .git in any case; no trailing slash, port, user, query or fragment. The key is host/org/repo, as written.
Types ¶
type Relation ¶
type Relation int
Relation is how one claimed address stands to another. Its zero value is Incomparable, not "apart": a Relation that nobody set, or that a caller did not think about, is read as a conflict. Callers that look for conflicts use Relation.Conflicts and never compare with Same or Under themselves.
const ( // Incomparable means an address is not plain ASCII or is longer than // MaxClaimLength, so it cannot be compared faithfully. It is a conflict. Incomparable Relation = iota // Apart means the two addresses are different and neither is under the other. Apart // Same means the addresses are the same once case and trailing slashes are // set aside. Same // Under means the first address sits under the second: the second followed // by a slash is a prefix of the first, so the boundary is a path segment // (/dbs/chinook2 is not under /dbs/chinook). Under // Over means the second address sits under the first. Over )
type Rule ¶
type Rule string
Rule names the rule that a refused input broke. It is stable: callers and tests match on it, never on the message.
const ( RuleLength Rule = "length" // longer than the bound of the function RuleNotURL Rule = "not-a-url" // empty, or not https://host/path at all RuleCharacter Rule = "character" // whitespace, a control character, a backslash or a non-ASCII byte RuleScheme Rule = "scheme" // a scheme other than https RuleUserinfo Rule = "userinfo" // credentials in the authority RulePort Rule = "port" // a port, even :443 RuleQuery Rule = "query" // a ? in the URL RuleFragment Rule = "fragment" // a # in the URL RulePercent Rule = "percent-escape" // a % in the URL RuleHostCharacter Rule = "host-character" // a character outside a-z 0-9 hyphen and dot in the host RuleHostCase Rule = "host-case" // an upper-case letter in the host RuleHostLabel Rule = "host-label" // an empty label, a label over 63 bytes, a hyphen at either end of a label RuleHostLength Rule = "host-length" // a host over 253 bytes RuleHostSingleLabel Rule = "host-single" // a host of one label RuleHostNumeric Rule = "host-numeric" // a host that is, or could be read as, an IP address RuleHostReserved Rule = "host-reserved" // a local, internal or reserved name RuleHostPunycode Rule = "host-punycode" // an xn-- label that is not valid punycode for a host name RuleHostIDN Rule = "host-idn" // an xn-- label of letters this tool does not accept yet RuleNoPath Rule = "no-path" // no path: write https://host/ RulePathCharacter Rule = "path-character" // a character outside A-Z a-z 0-9 . _ ~ / and - in the path RulePathEmptySeg Rule = "path-empty" // // RulePathDotSegment Rule = "path-dot" // a . or .. segment RulePlaceholder Rule = "placeholder" // {name} missing, repeated, or outside the path RuleHomepageLength Rule = "homepage-length" // a homepage over 200 bytes )
The rules a URL can break.
type URL ¶
type URL struct {
Host string // lower-case labels joined by dots
Path string // starts with /; for a template, {name} as written
Placeholder int // for a template, the offset of {name} in Path; otherwise -1
}
URL is an accepted URL in its parts, so that the rules that look at a host or a path (the marker of a canonical url, the same-origin rule, a publisher url) never split the text again. The scheme is always https and there is no port, userinfo, query or fragment.
func ParsePublicHTTPSURL ¶
ParsePublicHTTPSURL is PublicHTTPSURL that returns the parts of an accepted URL.
func ParsePublicHTTPSURLTemplate ¶
ParsePublicHTTPSURLTemplate is PublicHTTPSURLTemplate that returns the parts of an accepted URL.