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 EncodePathSegment(s string) string
- func GlobalDatabaseID(s string) error
- 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 IsLocalID(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 RecordsetName(s string) error
- func RecordsetPage(template, name string) error
- 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.
const MaxLocalIDLength = 40
MaxLocalIDLength is the longest descriptor localId.
const MaxRecordsetNameLength = 256
MaxRecordsetNameLength is the longest recordset name, in UTF-16 code units, as JavaScript's String.length counts them: the Directory's bound.
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 EncodePathSegment ¶ added in v0.30.0
EncodePathSegment is JavaScript's encodeURIComponent with the five characters ! ' ( ) * encoded too (as the Directory's encodePathSegment does): every byte of the UTF-8 text except A-Z a-z 0-9 - _ . ~ is written as %XX in upper case.
func GlobalDatabaseID ¶ added in v0.32.0
GlobalDatabaseID reports why s is not a global database identity, or nil: the canonical url of a database as the Directory takes it since it published global identities (globalDatabaseIdProblem in urls.mjs, 574a7ad and d089fa8). It is a public https URL under every rule of PublicHTTPSURL (a trailing slash is fine, a path of one segment or of several), with two differences. A path segment may hold percent escapes, when the segment is the canonical encoding of its text (encodeURIComponent, and the five characters ! ' ( ) * encoded too, in upper case; see EncodePathSegment) and the text, decoded again and again, never becomes ".", "..", a slash, a backslash or a control character. And a host under .example is allowed.
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 IsLocalID ¶ added in v0.33.0
IsLocalID reports whether s is the localId of a database descriptor, as the Directory has it (localIdPattern): a lower-case letter, then up to 39 lower-case letters, digits and hyphens (^[a-z][a-z0-9-]{0,39}$). Unlike IsID it allows a hyphen at the end and two in a row.
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 RecordsetName ¶ added in v0.30.0
RecordsetName reports why s is not a recordset name, or nil: text that is not blank by JavaScript's trim(), at most 256 UTF-16 code units, not "." or "..", and with no slash, backslash or control character (U+0000 to U+001F, U+007F). Any other character is allowed, a space, a dot and a non-ASCII letter among them.
func RecordsetPage ¶ added in v0.30.0
RecordsetPage reports why the page of the recordset called name is not an acceptable public URL, or nil. The page is the template of deployment.recordset_page with {name} replaced by the name as one encoded path segment. A name that needs no encoding makes an ordinary public https URL. One that does is accepted only when the segment is all that the {name} makes (nothing of the template shares its path segment), the template holds {name} in its path, the name is neither "." nor "..", has no slash, backslash or control character, and nothing inside it, decoded again and again, becomes one of those: a router that decodes a second time must not find a path separator or a dot segment. The rest of the URL is held to the ordinary rules; the page has no length bound of its own, as the Directory has none: the name is bounded here, and the template is bounded by the template rule (PublicHTTPSURLTemplate, 2048 bytes) that a caller runs on it first, so a caller that skips that rule has no bound on the page.
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.
const RuleRecordsetName Rule = "recordset-name"
RuleRecordsetName is the rule a refused recordset name broke. Like every Rule of this package it names the kind for callers and tests of the rules; a finding of the check carries its own code (manifest-recordsets).
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 ParseGlobalDatabaseID ¶ added in v0.32.0
ParseGlobalDatabaseID is GlobalDatabaseID that returns the parts of an accepted identity.
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.