1. Introduction
A ruleset is a set of rules for checking JSON and YAML documents. Each rule selects nodes in a document and checks each selected node with a function. A ruleset records the conventions a group of documents has to follow, so that the documents are checked the same way wherever they are written or reviewed. Nothing in the format depends on what the documents describe.
This specification defines the format apart from any one linter, so that several linters can implement it and agree on what a ruleset means.
1.1. How to read this document
The normative content of this document is its requirements, together with the definitions in § 3 Terminology, the conformance classes in § 2.2 Conformance classes, the tables that requirements refer to, and the ruleset schema that /req/ruleset/schema refers to. Everything else, including this introduction, examples, notes, and issues, is informative.
A requirement looks like this:
/req/example/identifiedrulesetexample
A requirement MUST be written so that a reader can tell, from the requirement alone, what conforms and what does not.Each requirement carries:
-
a requirement identifier, such as
/req/given/no-match. It is permanent (see § 2.3 Requirement identifiers), andlinks to it in every version of this document that contains it;#/req/given/no-match -
the conformance class it belongs to, which fixes whether it applies to a ruleset or to a linter;
-
the version it first appeared in;
-
the conformance tests that check it, listed below it.
A sentence without a keyword inside a requirement defines a term the keyword sentence uses, and binds with it.
The example above only shows the shape. Its status, example, marks that it is not a requirement of this specification. Recommendations (/rec/, written with should) and permissions (/per/, written with may) look the same and belong to a class too.
Every requirement is listed by class in Appendix A: Requirements index, and every test with its fixtures in Appendix B: Conformance tests. Open questions appear as issue blocks where they arise, and the Issues Index at the end of the document collects them. A release carries no issues.
1.2. Versions of this document
Releases are numbered as Semantic Versioning 2.0.0 [SEMVER] prescribes. A patch release corrects the text or the tests without changing what conforms, a minor release adds requirements that every ruleset conforming to the previous release still satisfies, which can include new requirements for linters, and a major release is anything else. Each release has its own URL and never changes once published.
What counts as a change that breaks conformance, and what is promised to whom, is discussed in discussion #12.
| URL | Serves |
|---|---|
https://spec.openlint.org/<version>/
| A release, such as 1.0.0.
|
https://spec.openlint.org/v<major>/draft/
| The working draft of a major version, such as v1 for 1.x. It changes without notice.
|
https://spec.openlint.org/v<major>/
| The latest release of a major version. Until the major version has a release, only the working draft’s ruleset.schema.json is served there.
|
https://spec.openlint.org/
| An index of every version. |
Each version publishes its own copy of the ruleset schema next to the document, as ruleset.schema.json. That copy is the one /req/ruleset/schema refers to, and its $id is its own URL. Editors and validators that follow the newest release can use https://spec.openlint.org/v<major>/ruleset.schema.json.
2. Conformance
2.1. Requirement keywords
These keywords appear only inside identified requirements. Text outside a requirement carries no normative force, whatever words it uses.
2.2. Conformance classes
A conformance target is the kind of thing a requirement applies to, either a ruleset or a linter.
- ruleset
-
A ruleset document. A ruleset conforms when it satisfies every requirement whose target is
ruleset. - linter
-
A linter. A linter conforms when it satisfies every requirement whose target is
linter.
A conformance class is a set of requirements for one conformance target. An implementation claims conformance to a class as a whole. Each class below names the classes it depends on. When it depends on a class of the same target, conforming to it includes conforming to that class.
A conformance claim names a release of this specification and the classes claimed, as https://spec.openlint.org/<version>/#/conf/<name>. A later release can add requirements to a class, so a claim holds for the release it names.
/conf/ruleset-corerulesetdepends on none
The ruleset format: what a ruleset document has to look like to be loaded by any conforming linter. Most of it is one requirement, validity against the ruleset schema. The other requirements are constraints a schema cannot express.8 requirements, 2 recommendations, 1 permission, 86 tests. Tests, index.
/conf/linter-corelinterdepends on ruleset-core
Everything a linter does with a ruleset and a document: loading, parsing and resolution, rule state, selecting documents and nodes, applying functions, and the results and messages it reports. It depends onruleset-core because a linter rejects a ruleset that does not conform to that class. § 6.2 Loading a ruleset says how.
/conf/linter-custom-functionslinterdepends on linter-core
The contract of custom functions, in any language: how a linter resolves a function a ruleset names, calls it, and reports what it returns. A linter claims it together with a binding./conf/linter-custom-functions-javascriptlinterdepends on linter-custom-functions
The JavaScript binding of custom functions: how a custom function is written as a JavaScript module, and where a linter finds it.2.3. Requirement identifiers
A requirement identifier has the form /req/<topic>/<name>. The topic is what the requirement is about, such as a ruleset key (given, overrides) or a step of linting (resolution, result), not a section of this document, so a requirement keeps its identifier when the document is reorganized. The name describes the requirement. Both are lowercase words joined by hyphens.
Identifiers are permanent:
-
An identifier is never reused for a different requirement.
-
When a requirement’s text changes in a way that changes what conforms, it gets a new identifier and the old one is marked removed.
-
A removed requirement stays listed in Appendix A: Requirements index as removed, with the version in which it was removed.
Whether path-style identifiers work for everyone is an open question in discussion #13. Alternatives such as numeric identifiers (REQ-042) or dotted names (given.no-match) have not been raised there yet.
2.4. Conformance tests
Every requirement has at least one conformance test, identified as /conf/<topic>/<name> in the same way. A test is data: a ruleset fixture, for linter tests a document fixture, and the expected outcome. Appendix B: Conformance tests renders every test from that data, and a linter is checked against the same data. A test covers one or more requirements of one class.
A test for a ruleset requirement expects a linter to load or to reject the fixture. A test for a linter requirement expects the results of linting the document fixture with the ruleset fixture, compared on the fields it names.
The tests follow these rules:
-
A ruleset requirement has an invalid test, whose fixture violates it and is rejected, and a valid test, whose fixture uses the same property and is loaded. The second shows that a linter does not reject more than the requirement says.
-
A linter requirement has a test that expects results, because a linter that does nothing passes a test that expects none.
-
A permission has a test in which what is permitted is accepted.
-
An invalid fixture violates one requirement only, so that its rejection proves that requirement and not another.
-
A recommendation has no test, because ignoring it still conforms.
Whether these tests become a published conformance suite, where its runner would live, and how a linter would publish its results against it, is not decided.
3. Terminology
- document
-
A JSON [RFC8259] or YAML [YAML-1.2.2] document that a linter lints. After parsing, both are the same tree of objects, arrays, strings, numbers, booleans, and null.
- ruleset
-
A JSON or YAML document in the format defined by this specification: a set of rules, the rulesets it extends, and adjustments to both.
- rule
-
A named unit of a ruleset that selects nodes in a document with
givenand asserts something about each of them withthen. - rule name
-
The key under which a rule appears in
rules. The name identifies the rule. It appears in results, and an extending ruleset uses it to adjust the rule. - path expression
-
A string in
giventhat selects nodes: a JSONPath expression beginning with$, or an alias reference beginning with.# - alias
-
A named, reusable set of path expressions, declared under
aliases. - alias reference
-
A path expression of the form
followed by an optional JSONPath remainder, such as#<name> .#Info.description - matched node
-
A node of the document selected by one of a rule’s path expressions.
- function
-
A named assertion applied to a matched node by
then. A function is either one of the core functions or a custom function. - custom function
-
A function whose implementation a ruleset supplies as code, named under
functions. - result
-
What a linter reports about a document: a problem a function found in a matched node, or a problem the linter found itself, such as a syntax error. A result has a code, a message, a severity, and a location in the document. See § 6.8 Results.
- severity
-
How a rule’s results are reported:
error,warn,info, orhint. The severityoffdisables the rule. - effective severity
-
The severity a rule has after every ruleset in the chain of
extendsand every override has adjusted it. - enabled
-
The state of a rule that a linter evaluates. A rule is either enabled or not, independently of its severity. A rule with effective severity
offis not enabled. - format
-
A named kind of document, such as
oas3_1for an OpenAPI 3.1 description. A rule scoped to formats runs only against documents of those kinds. - override
-
An entry of
overrides: adjustments that apply to the subset of documents matched by itsfilesglobs. - resolved document
-
The document after every
$refhas been replaced by the value it points to, including references into other files. See § 5.3 Reference resolution. - extension property
-
A property whose name begins with
x-, carried by the format for the author’s own tooling and ignored by a linter. - linter
-
Software that takes a ruleset and one or more documents and produces results.
- shorthand
-
A value in
rulesthat is not a Rule Object: a severity name or a boolean, which adjusts a rule the ruleset inherits instead of defining one. - mode
-
The second element of an
extendsentry,recommended,all, oroff, which decides which of the inherited rules start out enabled. - file-scoped override
-
An override whose
filesglobs do not all carry a JSON Pointer fragment. It can change anything a ruleset declares, for the documents it matches. - pointer-scoped override
-
An override whose
filesglobs all carry a JSON Pointer fragment. It can only change severities, at the locations the pointers name. - core function
-
A function this specification defines, which every conforming linter provides under its name.
- function invocation
-
One entry of a rule’s
then: the function to apply, thefieldto apply it to, and itsfunctionOptions. - problem
-
What a function returns for a value that fails: a message, and optionally the path of the offending value. A linter reports each problem as a result.
- requirement
-
An identified statement of this specification that one conformance target has to satisfy to conform. Recommendations and permissions are identified the same way but are not requirements.
4. The ruleset format
This part defines what a ruleset document looks like, in the order of the document itself: the document, the Ruleset Object at its root and each of its properties, the Rule Object and each of its properties.
4.1. The ruleset document
A ruleset is one file, written in JSON or YAML.
/req/ruleset/documentrulesetruleset-coresince this draft
A ruleset MUST be a JSON [RFC8259] or YAML [YAML-1.2.2] document.3 tests
| /conf/ruleset/document-json | A JSON text whose root is an object is valid. | valid |
| /conf/ruleset/document-object | A YAML document whose root is an object is valid. | valid |
| /conf/ruleset/document-syntax-error | A ruleset that is neither JSON nor YAML, here an unclosed flow sequence, is invalid. | invalid |
/req/ruleset/single-documentrulesetproposedruleset-coresince this draft
A ruleset written in YAML MUST contain exactly one YAML document.2 tests
| /conf/ruleset/document-single-yaml-document | A YAML stream with one document, with an explicit document marker, is valid. | valid |
| /conf/ruleset/document-two-yaml-documents | A YAML stream with two documents is invalid. | invalid |
/req/ruleset/single-document is proposed, because it changes current behavior. Spectral and OpenLint accept a YAML file with several documents separated by , read the first, and drop the others without reporting them; neither documents this. A ruleset split by a stray therefore loses rules silently, and rejecting the file catches that. Until both linters change, /conf/ruleset/document-two-yaml-documents fails against them. Whether this counts as a breaking change for 1.x is part of discussion #12.
The ruleset schema is written in JSON Schema 2020-12 and published with each version of this specification, next to this document as ruleset.schema.json. It validates the value at the root of the document.
/req/ruleset/schemarulesetruleset-coresince this draft
The root value of a ruleset MUST be valid against the ruleset schema of this version of this specification, as JSON Schema 2020-12 [JSON-SCHEMA-CORE-2020-12] [JSON-SCHEMA-VALIDATION-2020-12] defines validity.66 tests
| /conf/aliases/ | ||
|---|---|---|
| /conf/aliases/name-valid | An alias name of letters, digits, underscores, and hyphens starting with a letter is valid. | valid |
| /conf/aliases/scoped-form-valid | A scoped alias whose every target has formats and given is valid.
| valid |
| /conf/aliases/simple-form-valid | A simple alias that is an array of path expressions is valid. | valid |
| /conf/aliases/name | An alias name starting with a digit is invalid. | invalid |
| /conf/aliases/scoped-form-missing-formats | A scoped alias target without formats is invalid.
| invalid |
| /conf/aliases/simple-form-string | A simple alias given as a bare string is invalid. | invalid |
| /conf/custom-functions/ | ||
| /conf/custom-functions/declaration-valid | functions as an array of strings with a functionsDir string is valid.
| valid |
| /conf/custom-functions/declaration-not-array | A functions value that is a string is invalid.
| invalid |
| /conf/extends/ | ||
| /conf/extends/entry-forms-valid | A string entry and tuples with each of the three modes are valid. | valid |
| /conf/extends/entry-tuple-length | An extends tuple with a third element is valid; elements after the mode are ignored. | valid |
| /conf/extends/shape-array | A ruleset whose extends is an array mixing a string and a tuple is valid.
| valid |
| /conf/extends/shape-string | A ruleset whose extends is a single string is valid.
| valid |
| /conf/extends/entry-invalid-mode | An extends tuple with the mode strict is invalid.
| invalid |
| /conf/extends/shape-object | A ruleset whose extends is an object is invalid.
| invalid |
| /conf/formats/ | ||
| /conf/formats/shape-valid | formats as an array of strings on the ruleset, a rule, an override, and an alias target is valid.
| valid |
| /conf/formats/shape-string | A ruleset whose formats is a string is invalid.
| invalid |
| /conf/formats/unknown-identifier | A rule whose formats names an identifier outside the table, oas31, is invalid.
| invalid |
| /conf/given/ | ||
| /conf/given/path-expression-forms | A JSONPath expression starting with $ and an alias reference starting with are both valid.
| valid |
| /conf/given/present | A rule with given and then is valid.
| valid |
| /conf/given/shape-valid | given as a single path expression and as an array of them are both valid.
| valid |
| /conf/given/bare-property-name | A rule whose given is a bare property name is invalid.
| invalid |
| /conf/given/required | A rule with then but no given is invalid.
| invalid |
| /conf/given/shape-empty-array | A rule with is invalid.
| invalid |
| /conf/given/shape-object | A rule whose given is an object is invalid.
| invalid |
| /conf/overrides/ | ||
| /conf/overrides/file-scoped-valid | A file-scoped override with rules, extends, formats, parserOptions, and aliases is valid.
| valid |
| /conf/overrides/mixed-globs | An override whose files mix a pointer glob and a plain glob is file-scoped, so it can define rules with shorthands and is valid.
| valid |
| /conf/overrides/pointer-scoped-valid | A pointer-scoped override whose rules are severities only is valid.
| valid |
| /conf/overrides/shape-valid | An override with a non-empty files array is valid.
| valid |
| /conf/overrides/file-scoped-empty | A file-scoped override with neither extends nor rules is invalid.
| invalid |
| /conf/overrides/pointer-scoped-extends | A pointer-scoped override with extends is invalid.
| invalid |
| /conf/overrides/pointer-scoped-rule-object | A pointer-scoped override that defines a rule object is invalid. | invalid |
| /conf/overrides/shape-empty | An empty overrides array is invalid.
| invalid |
| /conf/overrides/shape-no-files | An override without files is invalid.
| invalid |
| /conf/parser-options/ | ||
| /conf/parser-options/shape-valid | Both parser options with severity values are valid. | valid |
| /conf/parser-options/shape-extension-property | An extension property inside parserOptions is invalid: the permission covers the root and rules only, and parserOptions admits no other properties.
| invalid |
| /conf/parser-options/shape-unknown-option | An unknown parser option is invalid. | invalid |
| /conf/parser-options/shape-unknown-severity | is invalid.
| invalid |
| /conf/rule/ | ||
| /conf/rule/all-properties | A rule using every property with its documented type is valid. | valid |
| /conf/rule/then-present | A rule with then and given is valid.
| valid |
| /conf/rule/recommended-not-boolean | A rule whose recommended is a string is invalid.
| invalid |
| /conf/rule/then-required | A rule with given but no then is invalid.
| invalid |
| /conf/rule/type-unknown | A rule whose type is neither style nor validation is invalid.
| invalid |
| /conf/rule/unknown-property | A rule with the unknown property severty is invalid.
| invalid |
| /conf/ruleset/ | ||
| /conf/ruleset/all-properties | A ruleset using every root property with its documented type is valid. | valid |
| /conf/ruleset/contributes-extends-only | A ruleset with only extends is valid.
| valid |
| /conf/ruleset/extension-root-property | A ruleset with the extension property x-owner at the root is valid.
| valid |
| /conf/ruleset/contributes-empty | A ruleset with only description is invalid.
| invalid |
| /conf/ruleset/description-not-string | A ruleset whose description is a number is invalid.
| invalid |
| /conf/ruleset/document-root-array | A ruleset whose root value is an array is invalid. | invalid |
| /conf/ruleset/documentation-url-intranet | A ruleset whose documentationUrl names a single-label intranet host, http://wiki/docs, is invalid.
| invalid |
| /conf/ruleset/documentation-url-invalid | A ruleset whose documentationUrl is not a URI is invalid.
| invalid |
| /conf/ruleset/documentation-url-mailto | A ruleset whose documentationUrl is a URI is invalid: only http, https, and ftp URLs are accepted.
| invalid |
| /conf/ruleset/rules-not-object | A ruleset whose rules is an array is invalid.
| invalid |
| /conf/ruleset/unknown-root-property | A ruleset with rules and the unknown root property rule is invalid.
| invalid |
| /conf/severity/ | ||
| /conf/severity/values-name | A rule with is valid.
| valid |
| /conf/severity/values-number | A rule with is valid.
| valid |
| /conf/severity/values-invalid | A rule with is invalid.
| invalid |
| /conf/shorthands/ | ||
| /conf/shorthands/values-valid | Every severity name and both booleans are valid in the shorthand position. | valid |
| /conf/shorthands/numeric | A numeric severity in the shorthand position is invalid. | invalid |
| /conf/shorthands/unknown-name | An unknown name in the shorthand position is invalid. | invalid |
| /conf/then/ | ||
| /conf/then/invocation-form-valid | An invocation with function, field, and functionOptions of the right types is valid.
| valid |
| /conf/then/shape-empty-array | A rule with is valid.
| valid |
| /conf/then/shape-valid | then as a single invocation and as an array of them are both valid.
| valid |
| /conf/then/function-missing | An invocation without function is invalid.
| invalid |
| /conf/then/options-not-object | An invocation whose functionOptions is a string is invalid.
| invalid |
| /conf/then/shape-string | A rule whose then is a string is invalid.
| invalid |
documentationUrl takes only an absolute http, https, or ftp URL with a public host, which is how the url format of OpenLint’s validator reads a URL. The schema spells that out as a pattern. It rejects and URIs, localhost, single-label intranet hosts such as http://wiki/docs, private and loopback addresses, and punycode host names; see /conf/ruleset/documentation-url-intranet. Accepting any absolute URI [RFC3986] would let a ruleset link to internal documentation.
4.2. The Ruleset Object
The Ruleset Object is the root of a ruleset. It holds the rules the ruleset defines, the rulesets it extends, and the adjustments it makes to them, and it can have a description and a documentationUrl.
| Property | Type | Description |
|---|---|---|
description
| string | Human-readable description of what the ruleset checks. |
documentationUrl
| string (URL) | URL to documentation for this ruleset. A rule that declares no documentationUrl of its own reports this URL followed by and the rule name.
|
formats
| array of strings, see Formats | Document formats this ruleset applies to. Acts as the default for every rule that does not declare its own formats. When omitted, rules apply to every document. |
extends
| string or array of strings or arrays, see Extends | Other rulesets this ruleset inherits from. |
rules
| map of objects or strings or booleans, see Rule Object or Shorthand | Maps a rule name to a Rule Object, or to a shorthand that adjusts a rule inherited through extends. The name appears in results, and an extending ruleset uses it to adjust the rule.
|
aliases
| map of objects or arrays of strings, see Aliases | Named, reusable path expressions that rules can reference from given with a leading .
|
overrides
| array of objects, see Overrides | File-scoped and pointer-scoped adjustments applied on top of the ruleset. |
parserOptions
| object, see Parser options | Severities for problems detected while parsing the document itself, before any rule runs. |
functions
| array of non-empty strings | Names of the custom functions the ruleset uses, loaded from functionsDir.
|
functionsDir
| string | Directory, relative to the ruleset file, that custom functions named in functions are loaded from. Defaults to functions.
|
x-*
| any | Extension property. |
At least one of extends, rules, or overrides is required. No other properties are allowed.
description : Acme API style guidedocumentationUrl : https://engineering.acme.com/api/governanceextends : [[spectral :oas ,recommended ]]formats : [oas3 ]rules : acme-operation-summary : description : Every operation needs a summary.severity : errorgiven : "$.paths[*]['get','put','post','delete','options','head','patch','trace']" then : field : summaryfunction : truthy
4.2.1. extends
extends inherits from one or more other rulesets. Its value is a string that identifies a ruleset, or an array. Each element of the array is such a string, or an array of the string and a mode. Elements after the mode are ignored.
Elements after the mode are accepted and ignored, so [ reads as [. OpenLint accepts them although its own validation schema forbids them for this array. Rejecting them would catch a misplaced bracket.
The string identifies a ruleset as a built-in identifier (spectral:oas, spectral:asyncapi, spectral:arazzo), a file path, an npm module specifier, or a URL. The mode decides which of the inherited rules start out enabled:
| Mode | Enables |
|---|---|
recommended
| Only rules with , which is the default for a rule. The default mode when a bare string is used.
|
all
| Every rule in the ruleset. |
off
| No rules. Inherit the definitions without turning any of them on, then enable the ones you want by name. |
extends : - [spectral :oas ,all ] - [./team-baseline.yaml ,"off" ] - https://example.com/rulesets/security.yaml
With off, a ruleset can extend a large ruleset and enable only the rules it names. § 6.2 Loading a ruleset defines loading, and § 6.4 Rule state: enabled and severity the effect of the modes.
4.2.2. rules
rules maps a rule name to either a Rule Object, which defines a rule or redefines an inherited one, or a shorthand, which adjusts a rule inherited through § 4.2.1 extends. Other rulesets refer to a rule by its name to disable it or change its severity, so renaming a rule breaks them.
A linter also reports problems of its own, such as a syntax error in the document, under fixed codes listed in § 6.8 Results. A rule with one of those names is accepted, but its results cannot be told apart from the linter’s own by their code.
/rec/rules/reserved-namesrulesetruleset-coresince this draft
A rule name SHOULD NOT be one of the codes a linter uses for the results it produces itself:parser, unrecognized-format, and invalid-ref.
4.2.2.1. Shorthands
A value in rules that is not an object is a shorthand: one of the severity names error, warn, info, hint, off, or one of the booleans true and false. It adjusts a rule instead of defining one.
/req/shorthands/inherited-onlyrulesetruleset-coresince this draft
A shorthand in the rootrules MUST name a rule the ruleset inherits through extends.
2 tests
| /conf/shorthands/inherited | A shorthand for a rule inherited through extends is valid.
| valid |
| /conf/shorthands/not-inherited | A shorthand for a rule the ruleset does not inherit is invalid. | invalid |
A shorthand in the rules of a file-scoped override names a rule the ruleset defines or inherits. That is checked when the override applies to a document; see /req/overrides/checked-when-applied.
Note: Severities are not written the same way everywhere. A rule’s severity takes a name or a number. A shorthand, in the root rules or in a file-scoped override, takes a name or a boolean but not a number. The rules of a pointer-scoped override take a name or a number but not a boolean.
A numeric shorthand such as is rejected only by OpenLint’s validation schema. Its type definitions and its merge code accept a number and would apply it as the severity. Accepting numbers in shorthands would remove the difference with severity without making any ruleset invalid.
extends : spectral:oasrules : operation-tags : error# raise it info-contact : false# switch it off oas3-schema : true# switch it back on, at its current severity
What each shorthand does is defined in § 6.4 Rule state: enabled and severity.
4.2.3. aliases
An alias names a list of path expressions, so that several rules can share it. A rule references an alias from given with a leading . Aliases are also under discussion in discussion #2.
An alias name starts with a letter, followed by letters, digits, _, and -. A simple alias is a non-empty array of path expressions.
/req/aliases/no-cyclerulesetruleset-coresince this draft
An alias MUST NOT refer to itself, directly or through other aliases.2 tests
| /conf/aliases/chain | An alias defined through another alias, without a cycle, is valid. | valid |
| /conf/aliases/cycle | Two aliases that refer to each other are invalid. | invalid |
aliases : Info : - $..infoInfoDescription : -"#Info.description" PathItemObject : - $.paths[*]OperationObject : -"#PathItemObject['get','put','post','delete','options','head','patch','trace']"
A scoped alias resolves to different expressions depending on the format of the document being linted. One rule can then select, for example, the parameters of both OpenAPI 2 and OpenAPI 3 documents. A scoped alias is an object:
| Property | Type | Required | Description |
|---|---|---|---|
targets
| array of objects, see Alias target | yes | One set of path expressions per set of formats. |
description
| string | Human-readable purpose of the alias. |
Each of its targets is an object:
| Property | Type | Required | Description |
|---|---|---|---|
formats
| array of strings, see Formats | yes | The formats this target applies to. |
given
| array of strings, see Path expression list | yes | The path expressions the alias resolves to for documents of those formats. |
aliases : ParameterObject : description : Every Parameter Object, wherever this spec version keeps them.targets : -formats : [oas2 ]given : - $.parameters[*] -formats : [oas3 ]given : - $.components.parameters[*]
Aliases are declared at the root of the ruleset or within a file-scoped override. How they expand is defined in § 6.6 Selecting nodes, and which rules an override’s aliases reach in § 6.3 Applying overrides.
4.2.4. overrides
Overrides apply adjustments to a subset of the documents being linted, matched by glob. overrides is a non-empty array of objects, each with a non-empty array files of glob patterns.
files entries are glob patterns, matched as minimatch 3.1.5 [MINIMATCH] matches them, against the path of the document relative to the directory of the ruleset file, with / as the separator. minimatch has no specification of its own, so the glob dialect is what that version accepts.
A glob dialect with a specification of its own, or a written subset of minimatch’s, would let a linter in another language match files without reproducing minimatch.
There are two forms, distinguished by whether every glob in files carries a JSON Pointer [RFC6901] fragment. An override that mixes the two kinds of glob is file-scoped.
An override whose files do not all carry a fragment is a file-scoped override. It has at least one of extends and rules, and it can also have formats, parserOptions, and aliases.
Note: The schema accepts other properties on a file-scoped override. When the override applies to a document, a property the Ruleset Object does not have makes a linter reject the ruleset, while one it does have, such as description, is accepted; see /req/overrides/checked-when-applied.
overrides : -files : ["legacy/**/*.yaml" ]rules : acme-operation-summary : warn -files : ["v3/**/*.yaml" ]formats : [oas3_1 ]extends : [[./strict.yaml ,all ]]
An override whose files all carry a fragment is a pointer-scoped override. It has rules, whose every value is a severity name or number, and no other property than files.
A pointer-scoped override can change a rule’s severity at one location in one file, or switch the rule off there. It cannot change what the rule checks. § 6.3 Applying overrides defines how overrides are applied.
4.2.5. parserOptions
Severities for problems found while parsing the document, before any rule runs; see § 5.2 Parsing.
| Property | Type | Description |
|---|---|---|
duplicateKeys
| number or string, see Severity | Severity for duplicate keys found in the document. Defaults to error.
|
incompatibleValues
| number or string, see Severity | Severity for a YAML mapping key that is not a string scalar. Defaults to error.
|
No other properties are allowed.
4.2.6. functions and functionsDir
A serialized ruleset names the custom functions it uses in functions, and can name the directory they load from in functionsDir; see § 7.2 Custom functions.
functions : - dictionaryfunctionsDir : ./my-functions# optional; defaults to "functions" rules : no-evil-words : message : "{{error}}" given : - $.info.title - $.info.descriptionthen : function : dictionary
/rec/custom-functions/prefer-corerulesetruleset-coresince this draft
A ruleset SHOULD use the core functions where they express the assertion, and name a custom function only for an assertion they cannot express.OpenLint runs custom functions without isolation. Whether a conforming linter has to sandbox them, or refuse functions that come from a remote ruleset, is open.
4.3. The Rule Object
A rule selects nodes in a document with given and checks each selected node with then.
| Property | Type | Required | Description |
|---|---|---|---|
given
| string or array of strings, see Path expression or list | yes | The part or parts of the document a rule is applied to. A single path expression, or an array of them. |
then
| object or array of objects, see Function invocation or list | yes | One function invocation, or an array of them, each applied to every node given matches. An empty array asserts nothing.
|
description
| string | Human-readable statement of what the rule requires. | |
documentationUrl
| string (URL) | URL explaining the rule and how to satisfy it. Reported alongside results. | |
message
| string | Template for the reported message. Supports the placeholders {{, {{, {{, {{, and {{.
| |
severity
| number or string, see Severity | Severity of this rule’s results. Defaults to warn.
| |
formats
| array of strings, see Formats | Document formats this rule applies to. Overrides the ruleset-level formats.
| |
recommended
| boolean | Whether the rule is enabled when another ruleset extends this one in recommended mode. Defaults to true.
| |
resolved
| boolean | Whether the rule runs against the document with $refs resolved (true, the default) or against the document as written (false). Set to false for rules about references themselves.
| |
tags
| array of strings | Free-form labels for grouping and filtering rules. | |
type
| style or validation
| The kind of rule. validation rules assert correctness; style rules assert convention.
| |
extensions
| object | Metadata for the author’s own tooling. A linter ignores it. | |
x-*
| any | Extension property. |
No other properties are allowed.
What message and description contribute to a result is defined in § 6.8 Results, how recommended combines with extends in § 6.4 Rule state: enabled and severity, and what resolved changes in § 6.6 Selecting nodes.
Note: tags, type, and extensions carry no behavior in this specification. They exist for tooling built around rulesets.
4.3.1. given
given says where in the document a rule looks. It is a JSONPath expression, such as $.paths[*]['get','put','post','delete','options','head','patch','trace'] for every operation of an OpenAPI description, or a reference to an alias that names one, such as . JSONPath-Plus has no grammar document, so the dialect is what that implementation accepts; § 6.6 Selecting nodes pins it. then checks each node the expression selects on its own. § 6.6 Selecting nodes defines how the expression is evaluated.
The value of given is a path expression or a non-empty array of them. A path expression that begins with $ is a JSONPath expression, and one that begins with is an alias reference.
/req/given/alias-definedrulesetruleset-coresince this draft
An alias reference in a rule MUST name an alias declared underaliases in the ruleset whose rules contain the rule or, for a rule in a file-scoped override, in that override or that ruleset. When the rule redefines a rule inherited through extends, the ruleset that defines the inherited rule MUST declare that alias too.
4 tests
| /conf/given/alias-declared | A rule referencing an alias the ruleset declares is valid. | valid |
| /conf/given/alias-redefined-both | A rule that redefines an inherited rule with an alias both rulesets declare is valid. | valid |
| /conf/given/alias-redefined-own | A rule that redefines an inherited rule with an alias only the extending ruleset declares is invalid. | invalid |
| /conf/given/alias-undefined | A rule referencing an alias the ruleset does not declare is invalid. | invalid |
A redefined rule keeps resolving its alias references against the ruleset that first defined it, so redefining an inherited rule with an alias reference works only when both rulesets declare the alias, and then uses the inherited ruleset’s. Resolving against the redefining ruleset’s aliases would let an extending ruleset retarget a rule with aliases of its own.
A bare property name such as paths is not a path expression. It begins with neither $ nor , so the ruleset schema rejects it, and so does a conforming linter.
4.3.2. then
then is one function invocation, or an array of them. A linter applies each of them to every node given matched, as § 6.7 Applying functions defines. A function invocation is an object:
| Property | Type | Required | Description |
|---|---|---|---|
function
| non-empty string | yes | Name of a core function (alphabetical, casing, defined, enumeration, falsy, length, or, pattern, schema, truthy, undefined, unreferencedReusableObject, xor) or of a custom function declared in functions.
|
field
| string | Apply the function to part of the matched node: a property name, a path of property names and indices such as info.contact.email or tags[0], or a JSONPath expression starting with $, evaluated against the matched node. @key applies the function to each property key of the matched node.
| |
functionOptions
| object | Options passed to the function. Each function defines the options it accepts. |
is accepted, and the rule then asserts nothing and never reports a result. Rejecting an empty array would catch a rule left unfinished.
/req/then/function-knownrulesetruleset-coresince this draft
function MUST name a core function or a custom function declared in functions.
2 tests
| /conf/then/function-core | A rule naming a core function is valid. | valid |
| /conf/then/function-unknown | A rule naming a function that is neither core nor declared is invalid. | invalid |
/req/then/function-options-validrulesetruleset-coresince this draft
Whenfunction names a core function, functionOptions MUST satisfy that function’s options in § 7.1 Core functions: only the options its table lists, with the types listed, every required option present, and no functionOptions at all for a function that takes no options.
4 tests
| /conf/then/options-valid | Core functions with options that match their tables are valid. | valid |
| /conf/then/options-on-no-option-function | truthy with options is invalid, because it takes none.
| invalid |
| /conf/then/options-required-missing | length with empty options is invalid, because it needs min or max.
| invalid |
| /conf/then/options-unknown-value | casing with , which is not a case the function knows, is invalid.
| invalid |
An unknown property inside an invocation, such as functionOption without the s, is ignored. When the function has no required option it runs with its defaults; when it has one, the ruleset is rejected for the missing functionOptions with a message that does not point at the misspelling. Rejecting the unknown property would catch the misspelling in both cases.
4.3.3. severity
severity says how serious a result from this rule is, and whether the rule runs at all. A ruleset can write either; results carry the numbers.
| Name | Number |
|---|---|
error
| 0
|
warn
| 1
|
info
| 2
|
hint
| 3
|
off
| -1
|
A rule’s severity is one of the names or one of the numbers.
What a severity means for a rule, and how it combines with extends, shorthands, and overrides, is defined in § 6.4 Rule state: enabled and severity.
4.3.4. formats
formats limits a rule to documents of certain kinds, so that a rule about OpenAPI paths does not run on an AsyncAPI document linted with the same ruleset.
formats is an array of format identifiers. It can appear on a ruleset, a rule, a file-scoped override, and an alias target.
The identifiers are listed in § 5.4 Formats; which rules run against which documents is defined in § 6.5 Selecting documents.
4.4. Extension properties
/per/extension-properties/anywhererulesetruleset-coresince this draft
A ruleset MAY have extension properties at its root and on any rule, with any value.2 tests
| /conf/extension-properties/accepted | A ruleset with extension properties of several types at the root and on a rule is accepted. | valid |
| /conf/parser-options/shape-extension-property | An extension property inside parserOptions is invalid: the permission covers the root and rules only, and parserOptions admits no other properties.
| invalid |
Use them to carry ownership, tickets, review dates, or anything else your own tooling needs. Rules also have an extensions object for the same purpose. A linter ignores both. Elsewhere they are not part of the format. The ruleset schema rejects them in parserOptions and in a pointer-scoped override. A file-scoped override, a scoped alias, and an invocation under then accept them, and a linter ignores them there too.
x-owner : platform-teamrules : acme-operation-summary : x-jira : GOV-142given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
5. Documents
A linter lints documents with the rulesets of § 4 The ruleset format. This section says what a document is to a linter, what happens to it before any rule runs, and how its formats are found.
5.1. The document model
A document is written in JSON [RFC8259] or YAML [YAML-1.2.2]. After parsing, both are the same tree of objects, arrays, strings, numbers, booleans, and null, and that tree is what path expressions select from and what functions see. A linter keeps the mapping from every node back to its position in the source text, because a result reports that position.
Note: Rulesets authored in JavaScript can inline function implementations and whole ruleset objects, which have no JSON representation and are out of scope. Only serialized documents and rulesets are specified.
The root of a document can be any value, including an array, a scalar, or null, which is what an empty file parses to. $ selects it whatever it is.
/req/document/any-rootlinterlinter-coresince this draft
A linter MUST lint a document whose root value is not an object, and$ MUST select that root value.
2 tests
| /conf/document/empty-file | An empty file is the document null, and a rule on $ sees it.
| 1 result |
| /conf/document/root-array | A document whose root is an array is linted, and $ selects the array.
| 1 result |
/req/document/encodinglinterlinter-coresince this draft
A linter MUST read a document and a ruleset as UTF-8.1 test
| /conf/document/utf8-keys | A document is read as UTF-8, so non-ASCII keys arrive intact in path.
| 2 results |
A UTF-8 byte order mark is accepted, but OpenLint then reports range columns on every line after the first one too far to the left. A UTF-16 document is not rejected; it parses as garbage and produces parser results. Ignoring a byte order mark and rejecting other encodings would make both deterministic.
5.2. Parsing
Problems in a document’s syntax are reported as results with the code parser. A problem that leaves the document with a JSON reading, such as a key that appears twice, still lets the document be linted; parserOptions sets its severity.
/req/parsing/syntax-errorlinterlinter-coresince this draft
When a document has a syntax error, a linter MUST produce a result with the codeparser and the severity error.
1 test
| /conf/parsing/syntax-error | A YAML document with an unclosed flow sequence is reported as a parser error.
| 1 result |
/req/parsing/several-documentslinterlinter-coresince this draft
When a YAML document contains several documents, a linter MUST produce a result with the codeparser and the severity error, and MUST lint the first document.
1 test
| /conf/parsing/several-documents | A YAML file with two documents gets a parser error, and the rules run on the first document.
| 2 results |
Whether rules run against the part of a document before a syntax error is not stated. OpenLint runs them against whatever tree its parser recovers, which for an unterminated flow collection can be nothing.
/req/parser-options/duplicate-keyslinterlinter-coresince this draft
When a document has a duplicate key, a linter MUST produce a result with the codeparser and the severity of duplicateKeys, error when it is not set, and MUST NOT produce one when it is off.
3 tests
| /conf/parser-options/duplicate-keys-default | A duplicate key is reported as an error by default. | 1 result |
| /conf/parser-options/duplicate-keys-warn | With , a duplicate key is reported as a warning.
| 1 result |
| /conf/parser-options/duplicate-keys-off | With , a duplicate key is not reported.
| 0 results |
/req/parser-options/incompatible-valueslinterlinter-coresince this draft
When a YAML document has a mapping key that is not a string scalar, such as1, true, or null, a linter MUST produce a result with the code parser and the severity of incompatibleValues, error when it is not set, and MUST NOT produce one when it is off.
1 test
| /conf/parser-options/incompatible-values | A YAML mapping key that is a number, not a string, has no JSON representation and is reported as an error by default. | 1 result |
Non-string scalar keys are the one construct OpenLint reports under incompatibleValues. A mapping key that is itself a sequence or a mapping is reported as a warning by the YAML parser, with no option controlling it, and a or tag ends the run with an error before any result. Whether those constructs belong under incompatibleValues, and whether a tag the linter cannot represent is a parser result, is open.
5.3. Reference resolution
The resolved document is the document after every $ref has been replaced by the value it points to, including references into other files. By default a rule sees the resolved document; a rule that sets sees the document as written.
A reference is an object with a $ref property whose value is a URI reference [RFC3986]: a fragment that is a JSON Pointer [RFC6901] into the same document, or a relative or absolute reference to another document, with an optional fragment into it.
/req/resolution/locallinterlinter-coresince this draft
A linter MUST replace a reference whose$ref is a fragment with the value the JSON Pointer names in the same document.
1 test
| /conf/resolution/local | A $ref to a pointer in the same document is replaced by the referenced schema, whose type the rule then sees.
| 1 result |
/req/resolution/externallinterlinter-coresince this draft
A linter MUST replace a reference whose$ref names another document with the value its fragment names in that document, after resolving the $ref against the location of the referencing document.
1 test
| /conf/resolution/external | A $ref to another file, relative to the document, is replaced by the referenced schema.
| 1 result |
/req/resolution/cyclelinterlinter-coresince this draft
A linter MUST resolve a document whose references form a cycle without failing, and MUST evaluate rules against it.1 test
| /conf/resolution/cycle | A schema that references itself resolves without error, and a rule sees its type.
| 1 result |
/req/resolution/unresolvablelinterlinter-coresince this draft
When a$ref cannot be resolved, a linter MUST produce a result with the code invalid-ref and the severity error, and MUST leave the reference object in place for the rules.
2 tests
| /conf/resolution/unresolvable | A $ref to a file that does not exist is reported as invalid-ref at severity error.
| 1 result |
| /conf/resolution/unresolvable-kept | After a $ref fails to resolve, a rule still sees the reference object.
| 2 results |
What a rule sees at the position of a reference in a cycle, and whether resolution follows $ref in every position or only where the document’s format expects one, is not stated.
5.4. Formats
A format names a kind of document, and a linter decides from the document whether it is of that kind, by the condition the table gives. A condition reads the document’s root object; a document whose root is not an object has no format. The string form of a value is the value as JSON writes it, without quotes for a string, so an unquoted 3.0 in YAML is the number 3. A rule scoped to a format runs only against documents of that format, so one ruleset can hold rules for OpenAPI 3.1 and for AsyncAPI 2.6 without either set running on the other kind of document. The table lists the identifiers a ruleset can use, which are the ones the ruleset schema accepts, and how each is detected.
| Family | Identifier | Detected when |
|---|---|---|
| OpenAPI | oas2
| swagger is present and its string form starts with 2 followed by a non-digit or the end.
|
oas3
| openapi is present and its string form starts with 3 followed by a non-digit or the end.
| |
oas3_0
| oas3, and the string form of openapi matches ^3\.0(\.[0-9]*)?$.
| |
oas3_1
| oas3, and the string form of openapi matches ^3\.1(\.[0-9]*)?$.
| |
oas3_2
| oas3, and the string form of openapi matches ^3\.2(\.[0-9]*)?$.
| |
| AsyncAPI | asyncapi2
| The same as aas2.
|
aas2
| asyncapi is present and its string form matches ^2\.(0|[1-9][0-9])\.(0|[1-9][0-9])$.
| |
aas2_0
| aas2, and the string form of asyncapi matches ^2\.0(\.[0-9]*)?$.
| |
aas2_1
| aas2, and the string form of asyncapi matches ^2\.1(\.[0-9]*)?$.
| |
aas2_2
| aas2, and the string form of asyncapi matches ^2\.2(\.[0-9]*)?$.
| |
aas2_3
| aas2, and the string form of asyncapi matches ^2\.3(\.[0-9]*)?$.
| |
aas2_4
| aas2, and the string form of asyncapi matches ^2\.4(\.[0-9]*)?$.
| |
aas2_5
| aas2, and the string form of asyncapi matches ^2\.5(\.[0-9]*)?$.
| |
aas2_6
| aas2, and the string form of asyncapi matches ^2\.6(\.[0-9]*)?$.
| |
aas3
| asyncapi is present and its string form matches ^3\.(0|[1-9][0-9])\.(0|[1-9][0-9])$.
| |
aas3_0
| aas3, and the string form of asyncapi matches ^3\.0(\.[0-9]*)?$.
| |
aas3_1
| aas3, and the string form of asyncapi matches ^3\.1(\.[0-9]*)?$.
| |
| Arazzo | arazzo1_0
| arazzo is present and its string form matches ^1\.0(\.[0-9]*)?$.
|
| JSON Schema | jsonSchema
| $schema is a string that contains //json-schema.org/.
|
jsonSchemaLoose
| jsonSchema, or type is a JSON Schema type name or an array of them, or enum is an array, or one of allOf, anyOf, oneOf, not, and if is an object or an array.
| |
jsonSchemaDraft4
| $schema matches ^https?://json-schema.org/draft-04/(hyper-)?schema#?$.
| |
jsonSchemaDraft6
| $schema matches ^https?://json-schema.org/draft-06/(hyper-)?schema#?$.
| |
jsonSchemaDraft7
| $schema matches ^https?://json-schema.org/draft-07/(hyper-)?schema#?$.
| |
jsonSchemaDraft2019_09
| $schema matches ^https?://json-schema.org/draft/2019-09/(hyper-)?schema#?$.
| |
jsonSchemaDraft2020_12
| $schema matches ^https?://json-schema.org/draft/2020-12/(hyper-)?schema#?$.
|
Any other identifier, such as the typo oas31, makes the ruleset invalid, wherever formats appears. How a rule names formats is defined in § 4.3.4 formats; how a linter detects them and which rules run is defined in § 6.5 Selecting documents.
The set of identifiers is closed, because OpenLint rejects an identifier it does not register. The previous document and schema accepted any string, so that formats added later, or registered by a host application, would stay valid. Which of the two this specification wants is open.
6. Linting
This part defines what a linter does with a ruleset and a document, in the order it happens:
-
Load the ruleset: check it and load what it extends.
-
Read the document: parse it, resolve its references, and detect its formats.
-
Apply the overrides whose globs match the document.
-
Work out the rule state: which rules are enabled, and at what severity.
-
Select documents: decide, by format, which rules apply to the document.
-
Select nodes: evaluate each rule’s
given. -
Apply functions: run each rule’s
thenon every node it matched. -
Report the results.
6.1. A running example
The sections of this part follow one scenario through the pipeline. A team keeps its shared rules in a base ruleset:
rules : operation-summary : description : Every operation needs a summary.severity : warngiven : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthyoperation-id-camel-case : description : operationId is written in camelCase.recommended : falsegiven : $.paths[*]['get','put','post','delete','options','head','patch','trace'].operationIdthen : function : casingfunctionOptions : type : camel
A project extends it. It switches on the rule the base leaves off, adds a rule of its own through an alias, scopes its own rules to OpenAPI 3, and raises one severity for the documents under legacy/:
extends : ./acme-base.yamlformats : [oas3 ]aliases : Operation : -"$.paths[*]['get','put','post','delete']" rules : operation-id-camel-case : trueoperation-has-tags : description : Every operation is tagged.severity : hintgiven : "#Operation" then : field : tagsfunction : definedoverrides : -files : ["legacy/**" ]rules : operation-summary : error
The document being linted is legacy/petstore.yaml. Its get operation has no summary, no tags, and a snake_case operationId; its post operation has all three:
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : operationId : list_petsresponses : "200" :description : OKpost : summary : Add a petoperationId : addPettags : [pets ]responses : "201" :description : Created
The scenario is also the conformance test /conf/linter/walkthrough, which checks the outcome this part describes.
6.2. Loading a ruleset
Loading turns the ruleset file into the set of rules that will run. A linter first checks the ruleset against § 4 The ruleset format. It then loads the rulesets it extends, recursively, so that their rules are available under their names. Overrides are applied next, per document; see § 6.3 Applying overrides.
acme-base.yaml and makes operation-summary and operation-id-camel-case available to the project ruleset, which adds operation-has-tags. The document’s path, legacy/petstore.yaml, matches the glob legacy/**, so the override applies to this document and sets operation-summary to error. For a document outside legacy/ the override would be ignored.
/req/linter/reject-invalidlinterlinter-coresince this draft
A linter MUST reject a ruleset that violates any requirement whose conformance target isruleset, or that extends, directly or through another ruleset’s extends, a ruleset that violates one, and MUST NOT produce results from it.
3 tests
| /conf/linter/accept-valid | A valid ruleset is loaded and linting produces its results. | 2 results |
| /conf/extends/base-invalid | A ruleset that extends a ruleset with an invalid given is rejected.
| rejected |
| /conf/linter/reject-invalid | Given an invalid ruleset, the linter rejects it and produces no results. | rejected |
/req/linter/accept-validlinterlinter-coresince this draft
A linter MUST load a ruleset that satisfies every requirement whose conformance target isruleset, when every ruleset it extends and every custom function it names can be loaded, unless /req/overrides/checked-when-applied has it rejected for the document being linted.
1 test
| /conf/linter/accept-valid | A valid ruleset is loaded and linting produces its results. | 2 results |
/req/extends/loadlinterlinter-coresince this draft
A linter MUST load every ruleset identified inextends and make each of its rules available to the extending ruleset under its rule name.
2 tests
| /conf/extends/load | A rule defined only in the extended ruleset is available to the extending one by name. | 2 results |
| /conf/linter/walkthrough | The running example of the Linting chapter: a ruleset that extends a base, enables a rule by shorthand, declares an alias, and overrides a severity for legacy documents, linting one legacy document. | 3 results |
/req/extends/unresolvablelinterlinter-coresince this draft
A linter MUST reject a ruleset whoseextends identifies a ruleset it cannot load.
2 tests
| /conf/extends/resolvable | A ruleset extending a file that exists is loaded and the inherited rule runs. | 1 result |
| /conf/extends/unresolvable | A ruleset extending a file that does not exist is rejected. | rejected |
How a linter resolves file paths, npm module specifiers, and URLs, and what each is relative to, is implementation-defined and has to be specified. So is whether every conforming linter has to recognize the built-in identifiers, which name rulesets shipped with OpenLint; discussion #6 uses openlint:openapi as the name of a new core OpenAPI ruleset. Resolving an npm module specifier also ties linter-core to the JavaScript package ecosystem, so it could move to a class of its own, as custom functions have.
In OpenLint, a cycle through extends between ruleset files is neither detected nor reported: the loader follows it until the process runs out of memory, so the ruleset never loads. Its core skips a definition it is already loading, but only for definitions held in memory. A conforming linter has to reject cycles, or the ruleset has to be invalid; which is open.
/req/extension-properties/ignoredlinterlinter-coresince this draft
A linter MUST NOT let an extension property change which results it produces.1 test
| /conf/extension-properties/ignored | Extension properties at the root and on a rule change nothing about the results. | 2 results |
6.3. Applying overrides
An override changes the ruleset for the documents its files globs match, so a linter applies overrides per document, after loading and before working out the rule state. A file-scoped override can add rulesets, rules, aliases, and formats; a pointer-scoped override only changes severities at the locations its pointers name.
/req/overrides/scopelinterlinter-coresince this draft
A linter MUST apply an override to a document only when the document’s path, relative to the directory of the ruleset file and with/ as the separator, matches one of the override’s files globs.
5 tests
| /conf/linter/walkthrough | The running example of the Linting chapter: a ruleset that extends a base, enables a rule by shorthand, declares an alias, and overrides a severity for legacy documents, linting one legacy document. | 3 results |
| /conf/overrides/scope-match | A document inside the override’s globs gets the override’s severity. | 2 results |
| /conf/overrides/scope-no-match | A document outside the override’s globs keeps the base severity. | 2 results |
| /conf/overrides/scope-outside-ruleset-directory | The same glob does not match a document in a legacy/ directory that is not next to the ruleset, whatever the working directory.
| 1 result |
| /conf/overrides/scope-ruleset-directory | A glob is matched against the document’s path relative to the ruleset file’s directory, so legacy/** matches a document in legacy/ next to the ruleset.
| 1 result |
/req/overrides/orderlinterlinter-coresince this draft
When several overrides match a document, a linter MUST apply them in array order, each on top of the previous, and all of them on top of the ruleset itself.1 test
| /conf/overrides/order | Of two overrides that both match, the later one wins. | 2 results |
/req/overrides/extendslinterlinter-coresince this draft
For a document a file-scoped override applies to, a linter MUST load the rulesets the override’sextends identifies and make their rules available in addition to those of the rulesets the ruleset extends, or, when the override has no rules, in place of them.
3 tests
| /conf/overrides/extends | An override’s extends adds rules for the documents it matches.
| 1 result |
| /conf/overrides/extends-with-rules | An applying override with extends and rules adds to the rulesets the ruleset extends: a, b, and r all run.
| 3 results |
| /conf/overrides/extends-without-rules | An applying override with extends and no rules replaces the rulesets the ruleset extends: b and the ruleset’s own r run, a does not.
| 2 results |
An override with extends and no rules drops, for the documents it matches, every rule the ruleset inherits through its own extends, while its own rules stay. With rules, it adds to them. Adding in both cases would make the two forms consistent.
/req/overrides/aliaseslinterlinter-coresince this draft
For a document a file-scoped override applies to, a linter MUST resolve the alias references of each rule the override defines and the ruleset does not against the override’saliases and the ruleset’s, the override’s alias taking precedence for a name both declare, and MUST resolve the alias references of every other rule as if the override declared no aliases.
3 tests
| /conf/overrides/aliases-existing-rule | An alias an override declares does not change a rule the ruleset defines: the rule keeps the ruleset’s alias and only its severity changes. | 1 result |
| /conf/overrides/aliases-new-rule | A rule an override defines resolves aliases against the override’s, which take precedence over the ruleset’s alias of the same name. | 1 result |
| /conf/overrides/aliases-outside-globs | Outside the override’s globs its aliases have no effect, and the rule keeps its default severity. | 1 result |
An alias declared in an override does not reach the rules the ruleset defines, so overriding an alias by name changes nothing for them. The previous document said an override’s aliases merge by name with the ruleset’s. Applying them to every rule, for the documents the override matches, would make that true.
/req/overrides/reject-foreign-propertylinterlinter-coresince this draft
For a document a file-scoped override applies to, a linter MUST reject the ruleset when the override has a property the Ruleset Object does not have.2 tests
| /conf/overrides/unknown-property-not-applied | The same override, applying to no document being linted, does not stop the ruleset from loading. | 1 result |
| /conf/overrides/unknown-property-applied | An override with a property the Ruleset Object does not have makes a linter reject the ruleset for a document it applies to. | rejected |
/req/overrides/reject-unknown-shorthandlinterlinter-coresince this draft
For a document a file-scoped override applies to, a linter MUST reject the ruleset when a shorthand in the override’srules names a rule the ruleset neither defines nor inherits.
3 tests
| /conf/shorthands/override-own-rule | A shorthand in an applying override for a rule the ruleset defines itself sets its severity. | 1 result |
| /conf/shorthands/override-unknown-not-applied | A shorthand for an unknown rule in an override that applies to no document being linted does not stop the ruleset from loading. | 1 result |
| /conf/shorthands/override-unknown | A shorthand in an override for a rule the ruleset neither defines nor inherits makes a linter reject the ruleset for a document the override applies to. | rejected |
/req/overrides/reject-undeclared-aliaslinterlinter-coresince this draft
For a document a file-scoped override applies to, a linter MUST reject the ruleset when a rule the override redefines has an alias reference to an alias that the ruleset defining the rule does not declare.2 tests
| /conf/overrides/redefined-rule-alias-declared | An override that redefines a rule with an alias the ruleset declares is loaded, and the redefined rule runs. | 1 result |
| /conf/overrides/redefined-rule-alias-applied | An override that redefines a rule with an alias only the override declares makes a linter reject the ruleset for a document it applies to. | rejected |
/req/overrides/checked-when-appliedlinterlinter-coresince this draft
A linter MUST NOT reject a ruleset for the reasons in the three requirements above when the override applies to no document being linted.2 tests
| /conf/overrides/unknown-property-not-applied | The same override, applying to no document being linted, does not stop the ruleset from loading. | 1 result |
| /conf/shorthands/override-unknown-not-applied | A shorthand for an unknown rule in an override that applies to no document being linted does not stop the ruleset from loading. | 1 result |
These three checks run only when the override applies to a document, as they do in OpenLint. Whether a Rule Object in an override’s rules merges with the rule it redefines, as /req/rules/redefinition-merge says for extends, or replaces it, is not tested. A ruleset with such an error loads without error until the first document the override matches. Everything else in an override is checked when the ruleset loads. Checking these three at load time too would report the error before any document matches.
/req/overrides/pointer-scopelinterlinter-coresince this draft
For a pointer-scoped override, a linter MUST apply the severities only to results whose path is at or below the location the pointer names.2 tests
| /conf/overrides/pointer-scope | A pointer-scoped override silences the rule under its pointer only. | 1 result |
| /conf/overrides/pointer-scope-at | A pointer that names the result’s own location silences it. | 1 result |
6.4. Rule state: enabled and severity
Every rule in the loaded ruleset ends up with two things: whether it is enabled, and its effective severity. Four mechanisms set them, in this order: the rule’s own definition, the mode its ruleset was extended with, the shorthands of each ruleset in the chain, and the overrides that match the document.
recommended.
| Rule | Defined as | After the mode | After the shorthand | After the override | Enabled | Effective severity |
|---|---|---|---|---|---|---|
operation-summary
| warn, recommended
| enabled | error
| yes | error
| |
operation-id-camel-case
| warn by default, not recommended
| not enabled | true: enabled, severity kept
| yes | warn
| |
operation-has-tags
| hint, own rule
| yes | hint
|
/req/severity/numeric-equivalencelinterlinter-coresince this draft
A linter MUST treat each numeric severity as equivalent to the name it is paired with in the table in § 4.3.3 severity.1 test
| /conf/severity/numeric-equivalence | A rule with reports with the severity of error.
| 2 results |
/req/severity/defaultlinterlinter-coresince this draft
When neither a rule nor any definition it redefines has aseverity, a linter MUST use warn.
2 tests
| /conf/extends/redefine-keeps-properties | A redefinition that sets only given and then keeps the inherited rule’s severity and message.
| 1 result |
| /conf/severity/default | A rule without severity reports with the severity of warn.
| 2 results |
/req/rules/recommendedlinterlinter-coresince this draft
A linter MUST enable every rule a ruleset defines in its ownrules whose recommended is true or absent, and MUST NOT enable one whose recommended is false unless a shorthand in an extending ruleset enables it.
2 tests
| /conf/rules/recommended-default | A rule the ruleset defines runs whether recommended is true or absent.
| 2 results |
| /conf/rules/recommended-false | A rule the ruleset defines with does not run, while its other rules do.
| 1 result |
/req/extends/mode-recommendedlinterlinter-coresince this draft
When a ruleset is extended with the moderecommended, or without a mode, a linter MUST enable exactly those rules inherited from it whose recommended is true or absent.
2 tests
| /conf/extends/mode-default | Without a mode, only the rule that is recommended runs. | 1 result |
| /conf/extends/mode-recommended | With mode recommended, only the rule that is recommended runs.
| 1 result |
/req/extends/mode-alllinterlinter-coresince this draft
When a ruleset is extended with the modeall, a linter MUST enable every rule inherited from it.
2 tests
| /conf/extends/mode-all | With mode all, both rules run.
| 2 results |
| /conf/extends/mode-all-extra-element | With [, the mode all applies and the extra element is ignored.
| 1 result |
/req/extends/mode-offlinterlinter-coresince this draft
When a ruleset is extended with the modeoff, a linter MUST make every rule inherited from it available and MUST NOT enable any of them.
1 test
| /conf/extends/mode-off | With mode off, no inherited rule runs until it is enabled by name.
| 1 result |
/req/extends/orderlinterlinter-coresince this draft
A linter MUST apply definitions and adjustments of a rule in this order: the rulesets identified byextends, in array order, then the extending ruleset’s own rules.
2 tests
| /conf/extends/precedence | The extending ruleset’s definition of r wins over both extended rulesets.
| 1 result |
| /conf/extends/precedence-order | Of two extended rulesets defining the same rule, the later one wins. | 1 result |
/req/rules/redefinition-mergelinterlinter-coresince this draft
When a Rule Object redefines a rule defined earlier in that order, a linter MUST replace each property the Rule Object has and MUST keep every property it does not have.1 test
| /conf/extends/redefine-keeps-properties | A redefinition that sets only given and then keeps the inherited rule’s severity and message.
| 1 result |
A redefinition keeps every property it does not set, so the base’s message or severity survives a redefinition that changes given and then, and can stop matching what the rule now checks. Replacing the whole rule would make a redefinition independent of the rule it replaces.
/req/shorthands/severitylinterlinter-coresince this draft
For a shorthand that is a severity name, a linter MUST set the rule’s effective severity to it and MUST enable the rule, or not enable it when the name isoff.
1 test
| /conf/shorthands/severity | A severity name enables the rule and sets its severity. | 1 result |
/req/shorthands/falselinterlinter-coresince this draft
For a shorthand that isfalse, a linter MUST NOT enable the rule and MUST leave its effective severity unchanged.
2 tests
| /conf/shorthands/false-control | Without the false shorthand, the inherited rule runs.
| 1 result |
| /conf/shorthands/false | false disables an enabled rule.
| 0 results |
/req/shorthands/truelinterlinter-coresince this draft
For a shorthand that istrue, a linter MUST enable the rule unless its effective severity is off, and MUST leave the effective severity unchanged, including a severity set by an earlier ruleset in the chain.
4 tests
| /conf/linter/walkthrough | The running example of the Linting chapter: a ruleset that extends a base, enables a rule by shorthand, declares an alias, and overrides a severity for legacy documents, linting one legacy document. | 3 results |
| /conf/shorthands/true-enables | true enables a rule that is off by default, at the severity it was defined with.
| 1 result |
| /conf/shorthands/true-keeps-severity | A rule disabled with false in one ruleset and re-enabled with true in the next keeps the severity it was defined with.
| 1 result |
| /conf/shorthands/true-off | true on a rule whose severity is off does not enable it.
| 0 results |
/req/severity/offlinterlinter-coresince this draft
A linter MUST NOT evaluate a rule that is not enabled, whether its effective severity isoff, a shorthand is false, or its recommended is false and nothing enables it, and MUST NOT produce results for it.
5 tests
| /conf/rules/recommended-false | A rule the ruleset defines with does not run, while its other rules do.
| 1 result |
| /conf/severity/off-control | The same rule at warn produces the results that off suppresses.
| 2 results |
| /conf/shorthands/false-control | Without the false shorthand, the inherited rule runs.
| 1 result |
| /conf/severity/off | A rule with produces no results although it would fail.
| 0 results |
| /conf/shorthands/false | false disables an enabled rule.
| 0 results |
6.5. Selecting documents
Not every rule applies to every document. A linter detects the formats of the document and keeps the rules that have no formats or whose formats include one of them, so that one ruleset can hold rules for several kinds of document. § 5.4 Formats says what a format is and which identifiers exist.
openapi : 3.1.0, so it is an OpenAPI 3.1 description. The only identifier the rulesets name is oas3, which the project ruleset sets with formats : [oas3 ] and its own rule operation-has-tags inherits, so the document’s formats are oas3. The base rules have no formats. All three rules apply. Linting an AsyncAPI document with the same ruleset produces one unrecognized-format warning, skips operation-has-tags, and still runs the two base rules.
/req/formats/identifierslinterlinter-coresince this draft
A linter MUST detect a format for a document when the document’s root is an object and the condition the table in § 5.4 Formats gives for the identifier holds, and MUST NOT detect it otherwise, among the identifiers /req/formats/detection-scope lets it detect.8 tests
| /conf/formats/detect-aas2-6 | A document with is aas2_6 and not aas2_5.
| 1 result |
| /conf/formats/detect-aas2-needs-patch | without a patch number is not aas2, while 2.6.0 is.
| 1 result |
| /conf/formats/detect-arazzo | A document with is arazzo1_0.
| 1 result |
| /conf/formats/detect-json-schema-2020-12 | A $schema of draft 2020-12 is jsonSchemaDraft2020_12 and not draft 7.
| 1 result |
| /conf/formats/detect-json-schema-loose | A document with and no $schema is jsonSchemaLoose but not jsonSchema.
| 1 result |
| /conf/formats/detect-number-version | An unquoted is the number 3, whose string form is 3, so the document is oas3 but not oas3_0.
| 1 result |
| /conf/formats/detect-oas2 | A document with is oas2 and not oas3.
| 1 result |
| /conf/formats/detect-oas3-1 | An OpenAPI 3.1 document is detected as oas3_1 and not as oas3_0.
| 1 result |
/req/formats/detection-scopelinterlinter-coresince this draft
A linter MUST detect a document’s formats only among the identifiers named in theformats of the ruleset, of its rules, and of the rulesets it extends and their rules. An identifier named only by a target of a scoped alias is never detected.
1 test
| /conf/formats/detection-declared-only | oas3_1 is not detected when no rule names it, so for an OpenAPI 3.1 document the alias uses its oas3 target, though an oas3_1 target comes last.
| 1 result |
Because detection is limited to the identifiers a ruleset names, a scoped alias whose target names a format no rule names never uses that target, even for a document of that format. Detecting every identifier in the table would make scoped aliases independent of the rules that use them.
The conditions in the table are the ones OpenLint applies. They read the string form of a version value, so written as a YAML number is OpenAPI 3 but not 3.0, and they accept . Conditions on the typed value would be stricter. Which document types the format supports is discussed in discussion #5.
/req/formats/rule-scopelinterlinter-coresince this draft
A linter MUST evaluate a rule that hasformats only against documents that have at least one of those formats.
1 test
| /conf/formats/rule-scope | Of two rules scoped to different formats, only the one whose format the document has runs. | 1 result |
/req/formats/ruleset-defaultlinterlinter-coresince this draft
A linter MUST treat a rule withoutformats, defined in a ruleset with formats, as having the formats of the ruleset that defines it, also when another ruleset extends it.
3 tests
| /conf/formats/ruleset-default | A rule without formats takes the ruleset’s, so it does not run on a document of another format, while a rule scoped to that format does.
| 1 result |
| /conf/formats/unrecognized-unscoped | A document with none of the ruleset’s formats gets the warning, the ruleset’s own rule does not run, and an unscoped rule from an extended ruleset does. | 2 results |
| /conf/linter/walkthrough | The running example of the Linting chapter: a ruleset that extends a base, enables a rule by shorthand, declares an alias, and overrides a severity for legacy documents, linting one legacy document. | 3 results |
/req/formats/rule-overrides-rulesetlinterlinter-coresince this draft
When both a rule and its ruleset haveformats, a linter MUST use the rule’s and ignore the ruleset’s for that rule.
1 test
| /conf/formats/rule-overrides-ruleset | A rule’s own formats replaces the ruleset’s.
| 1 result |
/req/formats/unscoped-runs-everywherelinterlinter-coresince this draft
A linter MUST evaluate a rule that has noformats, in a ruleset that has none, against every document, whatever its formats.
1 test
| /conf/formats/unscoped | Without formats anywhere, a rule runs on a document of no known format.
| 1 result |
/req/formats/unrecognized-documentlinterlinter-coresince this draft
When a ruleset hasformats, directly or through its rules, and a document has none of them, a linter MUST produce one result with the code unrecognized-format and the severity warn, and MUST still run every rule that has no formats of its own and inherits none from the ruleset.
2 tests
| /conf/formats/unrecognized-document | A document that has none of the ruleset’s formats gets one warning and no rule results. | 1 result |
| /conf/formats/unrecognized-unscoped | A document with none of the ruleset’s formats gets the warning, the ruleset’s own rule does not run, and an unscoped rule from an extended ruleset does. | 2 results |
6.6. Selecting nodes
For each rule that applies, the linter evaluates every path expression in given against the document and collects the nodes they select, the matched nodes. An alias reference is expanded first. A rule looks through $ref as if the referenced content were inline, unless it sets .
operation-has-tags has given : "#Operation" . The alias expands to $.paths[*]['get','put','post','delete'], which matches two nodes in the document: the get and the post operation under /pets. operation-summary matches the same two nodes through $.paths[*]['get','put','post','delete','options','head','patch','trace'], and operation-id-camel-case matches the two operationId strings.
/req/given/evaluatelinterlinter-coresince this draft
For every rule that is enabled for a document, a linter MUST evaluate each path expression ingiven against the document and apply the rule’s then to every matched node.
2 tests
| /conf/given/evaluate-all-matches | Two operations without summary produce one result each.
| 2 results |
| /conf/given/evaluate-multiple-expressions | Two path expressions in given each produce their results.
| 2 results |
/req/given/no-matchlinterlinter-coresince this draft
A linter MUST NOT produce a result for a rule whosegiven matches no node in the document.
2 tests
| /conf/given/match | The same rule on a document where given matches produces results.
| 1 result |
| /conf/given/no-match | A rule whose given matches nothing produces no results, even though its function would fail on any node.
| 0 results |
/req/given/resolvedlinterlinter-coresince this draft
A linter MUST evaluategiven against the resolved document, unless the rule sets resolved to false, in which case it MUST evaluate given against the document as written.
2 tests
| /conf/given/resolved-false | With , given sees the $ref object, which has no type.
| 1 result |
| /conf/given/resolved-default | By default given sees the resolved document, where the $ref has been replaced by a schema with type.
| 0 results |
/req/given/jsonpathlinterlinter-coresince this draft
A linter MUST evaluate a JSONPath expression as JSONPath-Plus 10.4.0 [JSONPATH-PLUS] evaluates it, including its filter expressions in parentheses, such as$..parameters[?(@.in === "header")].
1 test
| /conf/given/jsonpath-filter | A filter expression in parentheses, ?(@.in === "header"), selects the header parameter with an empty name; the index in its path is a string.
| 1 result |
JSONPath-Plus has no specification of its own, and its filter expressions are JavaScript. OpenLint evaluates most expressions with Nimma and falls back to JSONPath-Plus for the rest; a filter in the syntax of JSONPath [RFC9535], ?@.in == 'header', matches nothing in it. Making RFC 9535 normative would give the dialect a specification, at the cost of the rulesets that use JavaScript filters.
/req/given/once-per-nodelinterlinter-coresince this draft
A linter MUST apply a rule’sthen to a node once, even when several of the rule’s path expressions match it.
1 test
| /conf/given/once-per-node | A node matched by three path expressions of one rule is checked once. | 1 result |
/req/given/alias-expansionlinterlinter-coresince this draft
A linter MUST evaluate an alias reference as the set of the named alias’s path expressions, each with the remainder of the reference after the alias name appended to it.2 tests
| /conf/given/alias-expansion | is evaluated as $..info.description.
| 1 result |
| /conf/linter/walkthrough | The running example of the Linting chapter: a ruleset that extends a base, enables a rule by shorthand, declares an alias, and overrides a severity for legacy documents, linting one legacy document. | 3 results |
aliases : { Info : ["$..info" ] }, the alias reference #Info.description is evaluated as $..info.description.
/req/aliases/composelinterlinter-coresince this draft
When an alias’s path expressions contain alias references, a linter MUST expand those references in turn, until no reference remains.1 test
| /conf/aliases/compose | An alias defined through another alias expands to the full path. | 1 result |
/req/aliases/scopedlinterlinter-coresince this draft
For a scoped alias, a linter MUST use thegiven of the last target whose formats include a format of the document, and MUST match no node with it when no target does.
4 tests
| /conf/aliases/scoped-last-target | For an OpenAPI 3.1 document, which has both oas3 and oas3_1, only the last matching target is used.
| 1 result |
| /conf/aliases/scoped-no-target | A scoped alias with no target for the document’s formats matches nothing; only the format warning is reported. | 1 result |
| /conf/aliases/scoped-oas2 | On an OpenAPI 2 document, the scoped alias uses the oas2 target.
| 1 result |
| /conf/aliases/scoped-oas3 | On an OpenAPI 3 document, the scoped alias uses the oas3 target.
| 1 result |
Note: Aliases are resolved against the ruleset that defines the rule, not the ruleset that extends it. A rule inherited through extends keeps the aliases of the ruleset it came from.
Only the last matching target counts, so a document with two formats, such as oas3 and oas3_1, uses one target even when two match. Using every matching target, or the first, would read more naturally as a list.
6.7. Applying functions
A function is applied to one value: the matched node, or each value field selects in it, or each of its keys for @key. It receives that value and the invocation’s functionOptions, and it returns the problems it found, each with a message. Every problem becomes a result of the rule. A function that returns no problems passes. § 7.1 Core functions specifies each core function by what it asserts. A custom function has only this contract; see § 7.2 Custom functions.
get operation of the running example, operation-summary applies truthy to the summary property, which is absent, so the function returns one problem. operation-has-tags applies defined to tags, which is also absent, and gets one problem. operation-id-camel-case applies casing with type : camel to list_pets, which is not in camel case, and gets one problem. For the post operation all three functions pass. The three problems become three results.
/req/then/apply-eachlinterlinter-coresince this draft
For each matched node, a linter MUST apply every function invocation inthen, and MUST report each problem a function returns as a result.
2 tests
| /conf/then/apply-each | Two invocations that both fail on the same node produce two results. | 2 results |
| /conf/then/apply-each-problem | One invocation whose function returns two problems produces two results. | 2 results |
/req/then/fieldlinterlinter-coresince this draft
When an invocation hasfield, a linter MUST apply the function to each value field selects in the matched node: the property it names, the value at the path of property names and indices it names, or every value its JSONPath expression selects when it starts with $. When field selects nothing, the linter MUST apply the function once, to no value, and a result MUST have the path and range of the matched node. A result for a selected value MUST have the path of that value.
4 tests
| /conf/then/field | With field, the function sees the named property of the matched node.
| 1 result |
| /conf/then/field-jsonpath | A field that is a JSONPath expression applies the function to every value it selects.
| 2 results |
| /conf/then/field-missing | With field naming a property the node lacks, the function sees no value, so defined fails.
| 1 result |
| /conf/then/field-path | A field that is a path of property names reaches a nested value.
| 1 result |
/req/then/field-keylinterlinter-coresince this draft
Whenfield is @key, a linter MUST apply the function once to each property key of the matched node, and a result for a key MUST have the path of that property.
1 test
| /conf/then/field-key | With on a matched object, the function sees each of its property keys; the key that does not match the pattern is reported.
| 1 result |
Note: @key goes with a given that selects the parent. With and , the function checks every path. With , it would check the keys inside each path item.
/req/then/function-optionslinterlinter-coresince this draft
A linter MUST pass the invocation’sfunctionOptions to the function.
1 test
| /conf/then/function-options | The max option reaches the length function.
| 1 result |
6.8. Results
A linter reports what it found as results. This section defines the result model that discussion #13 asked for.
| Field | Type | Description |
|---|---|---|
code
| string | The rule name, or one of the linter’s own codes below. |
path
| array of strings | The keys and indices from the document root to the value the result is about. An index is written as a string, such as . An empty array stands for the document as a whole, as for unrecognized-format.
|
severity
| integer | The numeric effective severity: 0 to 3.
|
message
| string | The message, after placeholder substitution. |
range
| object | start and end, each with a zero-based line and character, locating the value in the source text. Characters are counted in UTF-16 code units, and end is just after the value.
|
source
| string | The location of the document, when it has one: a file path or URL. |
documentationUrl
| string | Where to read about the rule; see /rec/result/documentation-url. |
/req/result/fieldslinterlinter-coresince this draft
Every result a linter produces MUST havecode, path, severity, message, and range.
1 test
| /conf/result/fields | Every result has the five required fields. | 2 results |
/req/result/codelinterlinter-coresince this draft
Thecode of a result a rule produces MUST be its rule name. The code of a result the linter produces itself MUST be one of those in the table below.
1 test
| /conf/result/code | The code of a result is the rule name.
| 2 results |
| Code | Reported for |
|---|---|
parser
| A problem in the document’s syntax; see § 5.2 Parsing. |
unrecognized-format
| A document that has none of the ruleset’s formats; see § 6.5 Selecting documents. |
invalid-ref
| A $ref that cannot be resolved; see § 5.3 Reference resolution.
|
/req/result/severitylinterlinter-coresince this draft
Theseverity of a result MUST be the numeric form of the rule’s effective severity.
3 tests
| /conf/linter/walkthrough | The running example of the Linting chapter: a ruleset that extends a base, enables a rule by shorthand, declares an alias, and overrides a severity for legacy documents, linting one legacy document. | 3 results |
| /conf/result/severity | A rule with reports .
| 2 results |
| /conf/severity/result | A rule inherited at warn and raised to error reports with the severity of error.
| 2 results |
/req/result/rangelinterlinter-coresince this draft
Therange of a result MUST locate the value the result is about in the source text of the document: start at its first character and end just after its last, each with a zero-based line and a zero-based character counted in UTF-16 code units.
2 tests
| /conf/result/range | A result about the root value starts at line 0, character 0. | 1 result |
| /conf/result/range-utf16 | A character before the value that needs two UTF-16 code units counts as two, and the end is just after the value. | 1 result |
/req/result/sourcelinterlinter-coresince this draft
When the document was read from a file path or URL, every result for it MUST carry that location insource, as an absolute path or the URL.
1 test
| /conf/result/source | A result for a document read from a file carries that file’s path in source.
| 2 results |
/req/result/orderlinterlinter-coresince this draft
A linter MUST list results in the order of the documents they belong to, as the documents were given, and within a document by theline and then the character of range.start, and then by code.
1 test
| /conf/result/order | Results come in order of position, and by code at the same position, whatever the order of the rules. | 5 results |
/req/result/reference-targetlinterlinter-coresince this draft
When a rule ran against the resolved document and the value a result is about was reached through a reference, the result MUST carry thepath, range, and source of the value where it is written, and a value reached through several references MUST produce one result.
1 test
| /conf/resolution/shared-reference | A schema referenced from two operations fails a rule once, at the place where the schema is written. | 1 result |
/rec/result/documentation-urllinterlinter-coresince this draft
When the rule that produced a result has adocumentationUrl, a linter SHOULD carry it in the result’s documentationUrl, and when the rule has none but the ruleset that defines it has one, that URL followed by # and the rule name.
Indices in path are strings, [, as OpenLint reports them. Numbers would tell an index apart from a key that happens to be numeric.
6.8.1. The message
message takes five placeholders:
| Placeholder | Resolves to |
|---|---|
{{
| The message the function produced. |
{{
| The rule’s description.
|
{{
| The full path to the offending value. |
{{
| The key or index of the offending value. |
{{
| The offending value itself. |
/req/message/overridelinterlinter-coresince this draft
When a rule hasmessage, the message of every result it produces MUST be the rule’s message with each placeholder substituted.
1 test
| /conf/message/override | A rule’s message replaces the reported message.
| 2 results |
/req/message/placeholderslinterlinter-coresince this draft
A linter MUST substitute{{error }} with the message the function produced, {{description }} with the rule’s description, {{path }} with the path to the offending value as a JSON Pointer [RFC6901] with a leading # , {{property }} with the last key or index of that path, and {{value }} with the offending value.
3 tests
| /conf/message/placeholder-description | {{ resolves to the rule’s description.
| 2 results |
| /conf/message/placeholder-path | {{ is the JSON Pointer to the offending value, with a leading .
| 1 result |
| /conf/message/placeholder-property | {{ resolves to the key of the offending value.
| 1 result |
{{ substitutes a string as it is and an object as [, which no ruleset wants. A JSON serialization of the value would be useful, and is a change in output.
/req/message/defaultlinterlinter-coresince this draft
When a rule has nomessage, the message of every result it produces MUST be the rule’s description, or the message the function produced when the rule has no description.
1 test
| /conf/message/default-description | Without message, the result carries the rule’s description.
| 2 results |
source shortened. Severities are the effective ones from § 6.4 Rule state: enabled and severity, and every result points into the get operation.
[ { "code" : "operation-has-tags" , "path" : [ "paths" , "/pets" , "get" ], "message" : "Every operation is tagged." , "severity" : 3 , "range" : { "start" : { "line" : 6 , "character" : 8 }, "end" : { "line" : 10 , "character" : 25 } }, "source" : "legacy/petstore.yaml" }, { "code" : "operation-summary" , "path" : [ "paths" , "/pets" , "get" ], "message" : "Every operation needs a summary." , "severity" : 0 , "range" : { "start" : { "line" : 6 , "character" : 8 }, "end" : { "line" : 10 , "character" : 25 } }, "source" : "legacy/petstore.yaml" }, { "code" : "operation-id-camel-case" , "path" : [ "paths" , "/pets" , "get" , "operationId" ], "message" : "operationId is written in camelCase." , "severity" : 1 , "range" : { "start" : { "line" : 7 , "character" : 19 }, "end" : { "line" : 7 , "character" : 28 } }, "source" : "legacy/petstore.yaml" } ]
7. Functions
A rule names a function in then. It is either a core function, which every conforming linter provides, or a custom function, which a ruleset supplies as code. § 6.7 Applying functions defines how a function is applied.
7.1. Core functions
A conforming linter provides these functions under these names. Each function has a purpose, the values it applies to, its options, and one requirement that states what it asserts. The options tables are normative through /req/then/function-options-valid.
/req/functions/input-typeslinterlinter-coresince this draft
A core function MUST pass when the value it is applied to is absent or of a type the function does not apply to, unless its requirement below says otherwise.1 test
| /conf/functions/input-types | pattern passes on a number and on an absent value, and fails only on the string that does not match; xor passes on a string.
| 1 result |
7.1.1. truthy
The value is present and is not false, , 0, or null. Use it for required fields such as a summary or a contact.
It applies to any value, and to an absent one.
This function takes no options.
/req/functions/truthylinterlinter-coresince this draft
truthy MUST fail when the value is absent, false, "" , 0, or null, and MUST pass otherwise, including for an empty array and an empty object.
1 test
| /conf/functions/truthy | truthy fails on false, , 0, and null, and passes on an empty array, an empty object, and a string.
| 4 results |
operation-summary : given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
7.1.2. falsy
The value is absent, false, , 0, or null, so falsy passes where truthy fails. Use it to forbid a field, or to require that a flag is off.
It applies to any value, and to an absent one.
This function takes no options.
/req/functions/falsylinterlinter-coresince this draft
falsy MUST fail when the value is present and not one of false, "" , 0, or null, and MUST pass otherwise.
1 test
| /conf/functions/falsy | falsy fails on an empty array, an empty object, and a string, and passes on false, , 0, and null.
| 3 results |
Note: truthy and falsy follow ECMAScript truthiness [ECMA-262-2025]. Their requirements list the values in full because languages differ on whether an empty array or object is truthy.
no-deprecated-operations : given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : deprecatedfunction : falsy
7.1.3. defined
The value is present, whatever it is. Unlike truthy, an empty string or false passes.
It applies to any value, and to an absent one.
This function takes no options.
/req/functions/definedlinterlinter-coresince this draft
defined MUST fail when the value is absent and MUST pass when it is present, whatever it is.
1 test
| /conf/functions/defined | defined fails on an absent property and passes on a present one.
| 1 result |
7.1.4. undefined
The value is absent. Use it to forbid a property.
It applies to any value, and to an absent one.
This function takes no options.
/req/functions/undefinedlinterlinter-coresince this draft
undefined MUST fail when the value is present and MUST pass when it is absent.
1 test
| /conf/functions/undefined | undefined fails on a present property and passes on an absent one.
| 1 result |
7.1.5. pattern
The value is a string that matches, or does not match, a regular expression.
It applies to strings.
| Option | Type | Required | Default | Meaning |
|---|---|---|---|---|
match
| string | one of the two | A regular expression the value has to match. | |
notMatch
| string | one of the two | A regular expression; the value fails when it matches it. |
A regular expression is written bare, ^[a-z-]+$, or between slashes with flags, /^[a-z-]+$/i, with the flags of ECMAScript.
/req/functions/patternlinterlinter-coresince this draft
pattern MUST fail when the option match is given and the value does not match it, or when the option notMatch is given and the value matches it. Both options are regular expressions as defined by ECMAScript [ECMA-262-2025].
1 test
| /conf/functions/pattern | match fails on a string that does not match, notMatch fails on one that does.
| 2 results |
pattern takes the regular expressions and flags of ECMAScript, so a linter written in another language has to reproduce ECMAScript’s semantics. I-Regexp [RFC9485] is a dialect meant to behave the same in every language, but it has no lookaround or backreferences, so rulesets that use them would break. Which dialect a later version adopts is open.
paths-kebab-case : given : $.pathsthen : field : "@key" function : patternfunctionOptions : match : "^/[a-z-]+(/[a-z-{}]+)*$"
7.1.6. casing
The value is a string written in a named case convention.
It applies to non-empty strings.
| Option | Type | Required | Default | Meaning |
|---|---|---|---|---|
type
| string | yes | One of flat, camel, pascal, kebab, cobol, snake, macro.
| |
disallowDigits
| boolean | false
| When true, digits are not allowed anywhere in the value.
| |
separator
| object | char, a single character that separates groups of words and is required in the object, and allowLeading, whether the value can start with it.
|
/req/functions/casinglinterlinter-coresince this draft
casing MUST fail when the value is a non-empty string that is not written in the case named by the option type: one of flat, camel, pascal, kebab, cobol, snake, and macro, where only ASCII letters and digits count as written in a case.
3 tests
| /conf/functions/casing | casing with fails on a camel-case string.
| 1 result |
| /conf/functions/casing-non-ascii | Only ASCII letters count, so éclair is not camel case; the empty string passes.
| 1 result |
| /conf/functions/casing-types | Each case accepts a value written in it and rejects one written in another case. | 6 results |
The exact character classes of each case have to be stated before this requirement is complete.
operation-id-camel-case : given : $.paths[*]['get','put','post','delete','options','head','patch','trace'].operationIdthen : function : casingfunctionOptions : type : camel
7.1.7. length
The size of the value is within bounds: the number of characters of a string, of elements of an array, of properties of an object, or the value of a number.
It applies to strings, arrays, objects, and numbers.
| Option | Type | Required | Default | Meaning |
|---|---|---|---|---|
min
| number | one of the two | The smallest length that passes. | |
max
| number | one of the two | The largest length that passes. |
/req/functions/lengthlinterlinter-coresince this draft
length MUST fail when the length of the value is below the option min or above the option max, where the length of a string is its number of UTF-16 code units, of an array its number of elements, of an object its number of properties, and of a number the number itself.
3 tests
| /conf/functions/length | length with fails on a string of six characters and an array of five elements, and passes on a number below the bound.
| 2 results |
| /conf/functions/length-code-units | A string’s length is its number of UTF-16 code units: é is one, an emoji two.
| 1 result |
| /conf/functions/length-min | length with fails on an object with one property and a string of one character, and passes on an object with two.
| 2 results |
short-summary : given : $.paths[*]['get','put','post','delete','options','head','patch','trace'].summarythen : function : lengthfunctionOptions : max : 120
7.1.8. alphabetical
The elements of an array, or the keys of an object, are in ascending order.
It applies to arrays and objects.
| Option | Type | Required | Default | Meaning |
|---|---|---|---|---|
keyedBy
| string | For an array of objects, the property to compare the elements by. |
/req/functions/alphabeticallinterlinter-coresince this draft
alphabetical MUST fail when the elements of an array, or the keys of an object, are not in ascending order, where numbers are compared by value, strings as localeCompare of ECMAScript [ECMA-402] compares them with no locale given, and two values of different types count as in order. With the option keyedBy, the elements of an array of objects are compared by that property.
4 tests
| /conf/functions/alphabetical | alphabetical fails on an unsorted array and passes on a sorted one.
| 1 result |
| /conf/functions/alphabetical-case | Strings are compared with localeCompare, so a comes before B and B after a.
| 1 result |
| /conf/functions/alphabetical-keyed-by | With keyedBy, an array of objects is compared by that property.
| 1 result |
| /conf/functions/alphabetical-numbers | Numbers are compared by value, so [ is in order and [ is not.
| 1 result |
localeCompare without a locale uses the locale of the environment, so the order of strings such as ä and z can differ between machines. A fixed collation, such as code point order, would make the function portable at the cost of changing the order of mixed-case names.
7.1.9. enumeration
The value is one of a fixed set.
It applies to strings, numbers, booleans, and null.
| Option | Type | Required | Default | Meaning |
|---|---|---|---|---|
values
| array of string, number, boolean, or null | yes | The values that pass. |
/req/functions/enumerationlinterlinter-coresince this draft
enumeration MUST fail when the value is not one of the elements of the option values, compared by JSON type and value, so that "1" is not 1 while 1.0 is.
2 tests
| /conf/functions/enumeration | enumeration fails on a value outside values.
| 1 result |
| /conf/functions/enumeration-types | is not one of [, while 1.0 is.
| 1 result |
audience-known : given : $.info.x-audiencethen : function : enumerationfunctionOptions : values : [internal ,partner ,public ]
7.1.10. schema
The value validates against a JSON Schema. Use it when the shape to assert is more than one property deep.
It applies to any present value, and fails on an absent one.
| Option | Type | Required | Default | Meaning |
|---|---|---|---|---|
schema
| object | yes | The JSON Schema to validate against. | |
dialect
| string | auto
| The draft to read the schema in: one of draft4, draft6, draft7, draft2019-09, draft2020-12, or auto, which takes it from $schema and falls back to draft7.
| |
allErrors
| boolean | false
| Report every validation error as a problem, not only the first. |
The drafts dialect accepts, and the specification of each:
| Dialect | Specification |
|---|---|
draft4
| [JSON-SCHEMA-DRAFT-04] |
draft6
| [JSON-SCHEMA-DRAFT-06] |
draft7
| [JSON-SCHEMA-DRAFT-07] |
draft2019-09
| [JSON-SCHEMA-DRAFT-2019-09] |
draft2020-12
| [JSON-SCHEMA-CORE-2020-12] and [JSON-SCHEMA-VALIDATION-2020-12] |
/req/functions/schemalinterlinter-coresince this draft
schema MUST fail when the value is absent, or when it does not validate against the JSON Schema given as the option schema, asserting the format keyword and resolving a $ref within the option against the option itself, with one problem for the first failed keyword, or one per failed keyword when the option allErrors is true. The schema is read in the draft the option dialect names, as the table above defines each draft; with dialect absent or auto, in the draft its $schema names, and in draft 7 when it names none.
8 tests
| /conf/functions/schema | schema fails on a value that does not validate against the given JSON Schema.
| 1 result |
| /conf/functions/schema-absent | schema fails on an absent value.
| 1 result |
| /conf/functions/schema-all-errors | With , a value that fails two keywords gets two problems.
| 2 results |
| /conf/functions/schema-default-draft | Without $schema or dialect, the schema is read as draft 7: prefixItems is ignored, required is not.
| 1 result |
| /conf/functions/schema-dialect | With , prefixItems applies.
| 1 result |
| /conf/functions/schema-first-error | Without allErrors, a value that fails two keywords gets one problem.
| 1 result |
| /conf/functions/schema-format | The format keyword is asserted, so not-an-email fails .
| 1 result |
| /conf/functions/schema-ref | A $ref inside the option resolves against the option’s own $defs.
| 1 result |
contact-shape : given : $.info.contactthen : function : schemafunctionOptions : schema : type : objectrequired : [name ,properties : : type : stringformat : email
7.1.11. xor
Exactly one of a set of properties is present.
It applies to objects.
| Option | Type | Required | Default | Meaning |
|---|---|---|---|---|
properties
| array of at least two strings | yes | The property names, of which exactly one has to be present. |
/req/functions/xorlinterlinter-coresince this draft
xor MUST fail unless exactly one of the properties named by the option properties is present on the value, where a property is present when the object has the key, whatever its value.
2 tests
| /conf/functions/xor | xor fails unless exactly one of properties is present.
| 2 results |
| /conf/functions/xor-null | A key whose value is null counts as present, so { has two of the properties.
| 1 result |
example-or-examples : given : $.paths[*]['get','put','post','delete','options','head','patch','trace'].responses[*].content[*]then : function : xorfunctionOptions : properties : [example ,examples ]
7.1.12. or
At least one of a set of properties is present.
It applies to objects.
| Option | Type | Required | Default | Meaning |
|---|---|---|---|---|
properties
| array of at least two strings | yes | The property names, of which at least one has to be present. |
/req/functions/orlinterlinter-coresince this draft
or MUST fail unless at least one of the properties named by the option properties is present on the value, where a property is present when the object has the key, whatever its value.
2 tests
| /conf/functions/or | or fails unless at least one of properties is present.
| 1 result |
| /conf/functions/or-null | A key whose value is null counts as present, so { satisfies or and {} does not.
| 1 result |
operation-documented : given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : function : orfunctionOptions : properties : [summary ,description ]
7.1.13. unreferencedReusableObject
Every member of a container of reusable definitions is referenced somewhere. Use it to find unused schemas, parameters, or responses.
It applies to objects.
| Option | Type | Required | Default | Meaning |
|---|---|---|---|---|
reusableObjectsLocation
| string | yes | A JSON Pointer fragment to the container, such as or .
|
/req/functions/unreferenced-reusable-objectlinterlinter-coresince this draft
unreferencedReusableObject, applied to an object of reusable definitions, MUST fail once for each member that no $ref in the documents being linted points to, where the option reusableObjectsLocation is the JSON Pointer [RFC6901] of that object in the document.
1 test
| /conf/functions/unreferenced-reusable-object | unreferencedReusableObject reports a member of the container that nothing references.
| 1 result |
Note: The function counts references, which no longer exist in the resolved document, so the rule in the example sets . Members referenced only by documents outside the lint run count as unreferenced.
no-unused-schemas : resolved : falsegiven : $.components.schemasthen : function : unreferencedReusableObjectfunctionOptions : reusableObjectsLocation : "#/components/schemas"
7.2. Custom functions
A ruleset declares the custom functions it uses as described in § 4.2.6 functions and functionsDir.
Custom functions are specified in two parts. The contract, the class /conf/linter-custom-functions, says how a linter resolves a function a ruleset names, calls it, and reports what it returns, whatever language the function is written in. A binding says how a function is written and found in one language. This version defines one binding, for JavaScript, the class /conf/linter-custom-functions-javascript. A linter claims the contract together with a binding, and can decline both.
A linter written in Go needs a Go binding, and this version defines none. vacuum, a linter written in Go, loads custom functions as Go plugins from a directory named on its command line, registered under the name each function reports, and does not read functions or functionsDir. A Go binding would have to say where a linter finds a function, which symbol it loads, and the function’s signature in types this specification owns. Go plugins also load only into a binary built with the same Go version and package versions, so a plugin built for one linter does not load into another.
A binding ties a function to one language, so a ruleset whose functions are written in that language runs only on linters that bind it. A binding to WebAssembly, or to a function that runs as a separate process, would let any linter run functions written in any language that targets it. Whether a later version defines such a binding is open.
7.2.1. The contract
/req/custom-functions/missinglinterlinter-custom-functionssince this draft
A linter MUST reject a ruleset that names a function it cannot load.2 tests
| /conf/custom-functions/present | A ruleset naming a function whose file exists is loaded and the function runs. | 1 result |
| /conf/custom-functions/missing | A ruleset naming a function whose file does not exist is rejected. | rejected |
/req/custom-functions/invokelinterlinter-custom-functionssince this draft
A linter MUST call a custom function with the value the invocation targets and the invocation’sfunctionOptions, and MUST report each problem the function returns as a result whose message is the problem’s message.
1 test
| /conf/custom-functions/invoke | The function receives the value and the options, and each problem it returns becomes a result with its message. | 2 results |
/req/custom-functions/problem-pathlinterlinter-custom-functionssince this draft
A linter MUST report a problem that carries apath at that path in the document, a problem without one at the matched node, and a problem whose path is not in the document at the document’s root.
1 test
| /conf/custom-functions/problem-path | A problem with a path is reported there; one whose path is not in the document is reported at the root.
| 2 results |
/req/custom-functions/no-problemslinterlinter-custom-functionssince this draft
A linter MUST treat a custom function that returns nothing as returning no problems.1 test
| /conf/custom-functions/returns-nothing | A function that returns nothing reports no problems, while one that returns a problem does. | 1 result |
A function that throws, or returns something that is not a list of problems, ends the run with no results and an error. Reporting it as a result for the rule, with the error as its message, would keep the results of the other rules.
Note: The contract is tested through the JavaScript binding, the only binding this version defines, so the fixtures of its tests are JavaScript functions.
7.2.2. JavaScript binding
/req/custom-functions/javascript-loadlinterlinter-custom-functions-javascriptsince this draft
A linter MUST load each name infunctions from <functionsDir>/<name>.js, resolved relative to the ruleset file, with functions as the directory when functionsDir is absent.
2 tests
| /conf/custom-functions/load-custom-dir | With functionsDir, the function is loaded from that directory.
| 1 result |
| /conf/custom-functions/load-default-dir | A function named in functions is loaded from functions/<name>.js next to the ruleset.
| 1 result |
/req/custom-functions/javascript-promiselinterlinter-custom-functions-javascriptsince this draft
When a JavaScript custom function returns a promise, a linter MUST wait for it and use the problems it resolves to.1 test
| /conf/custom-functions/javascript-promise | A function that returns a promise is awaited, and the problems it resolves to are reported. | 1 result |
A custom function is a JavaScript module whose export is the function. It receives the value and the options, and returns an array of problems, each an object with a message; an empty array means the value passes.
// functions/forbiddenWords.js module. exports= ( value, options) => { const problems= []; for ( const wordof options. forbidden) { if ( String( value). includes( word)) { problems. push({ message: `Contains " ${ word} ".` }); } } return problems; };
A function module in CommonJS and one in ES module syntax both load in OpenLint, and the function receives a third argument with the document, the rule, and the path. Neither is stated normatively.
8. Security and privacy considerations
A ruleset is configuration, but loading one can read files, fetch URLs, and run code. Anyone who lints with a ruleset they did not write, or extends one, trusts its author with all of that. This section collects what a linter and its users have to be aware of. It states no requirements.
Custom functions run code. A ruleset that names custom functions depends on code, and its document alone does not say what those rules check. This specification does not require a linter to isolate that code, so extending an untrusted ruleset that has custom functions runs that code with the linter’s privileges, and functionsDir resolved relative to a remote ruleset loads code from that remote location.
extends fetches rulesets. An extends entry that is a URL makes the linter fetch it, and an npm module specifier makes it load a package. Nothing in the format pins what is fetched, so a ruleset fetched twice can differ, and a compromised host or package changes what every document is checked against. A ruleset that is extended by file path is read from wherever the path leads.
Path expressions and regular expressions evaluate input. A JSONPath-Plus filter expression in given is JavaScript evaluated by the linter, and a pattern option is an ECMAScript regular expression, which can take exponential time on crafted input. Both come from the ruleset, so a ruleset is as trusted as code even without custom functions.
References read other documents. Resolving a $ref to an external file or URL reads it, so linting an untrusted document can make the linter read any file it has access to and fetch from any host it can reach. The schema function’s option can carry $ref too.
Results carry locations. source is the location of the document, which can be an absolute path or a URL, and results are often written to logs and CI output that others can read.
Appendix A: Requirements index
4 conformance classes, 93 requirements, 3 recommendations, 1 permission, and 238 conformance tests. The same data is available as requirements.json, and the tests with their fixtures under conformance/.
/conf/ruleset-core
The ruleset format: what a ruleset document has to look like to be loaded by any conforming linter. Most of it is one requirement, validity against the ruleset schema. The other requirements are constraints a schema cannot express. Target: ruleset Depends on: none.
/conf/linter-core
Everything a linter does with a ruleset and a document: loading, parsing and resolution, rule state, selecting documents and nodes, applying functions, and the results and messages it reports. It depends on ruleset-core because a linter rejects a ruleset that does not conform to that class. #loading says how. Target: linter Depends on: ruleset-core.
/conf/linter-custom-functions
The contract of custom functions, in any language: how a linter resolves a function a ruleset names, calls it, and reports what it returns. A linter claims it together with a binding. Target: linter Depends on: linter-core.
| Identifier | Section | Tests | Since |
|---|---|---|---|
| /req/custom-functions/missing | The contract | /conf/custom-functions/present, /conf/custom-functions/missing | this draft |
| /req/custom-functions/invoke | The contract | /conf/custom-functions/invoke | this draft |
| /req/custom-functions/problem-path | The contract | /conf/custom-functions/problem-path | this draft |
| /req/custom-functions/no-problems | The contract | /conf/custom-functions/returns-nothing | this draft |
/conf/linter-custom-functions-javascript
The JavaScript binding of custom functions: how a custom function is written as a JavaScript module, and where a linter finds it. Target: linter Depends on: linter-custom-functions.
| Identifier | Section | Tests | Since |
|---|---|---|---|
| /req/custom-functions/javascript-load | JavaScript binding | /conf/custom-functions/load-custom-dir, /conf/custom-functions/load-default-dir | this draft |
| /req/custom-functions/javascript-promise | JavaScript binding | /conf/custom-functions/javascript-promise | this draft |
Appendix B: Conformance tests
Tests are grouped under the requirement they cover. Open a test to see its fixtures and expected outcome.
Conformance class /conf/ruleset-core
/req/ruleset/documentrulesetin section The ruleset document
A ruleset MUST be a JSON RFC8259 or YAML YAML-1.2.2 document.
/conf/ruleset/document-jsonA JSON text whose root is an object is valid.valid
Covers /req/ruleset/document. Fixtures in conformance/ruleset/document-json/.
{ "rules" : { "acme-operation-summary" : { "given" : "$.paths[*][*]" , "then" : { "field" : "summary" , "function" : "truthy" } } } }
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/ruleset/document-objectA YAML document whose root is an object is valid.valid
Covers /req/ruleset/document. Fixtures in conformance/ruleset/document-object/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/ruleset/document-syntax-errorA ruleset that is neither JSON nor YAML, here an unclosed flow sequence, is invalid.invalid
Covers /req/ruleset/document. Fixtures in conformance/ruleset/document-syntax-error/.
rules : r : [
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/req/ruleset/single-documentrulesetproposedin section The ruleset document
A ruleset written in YAML MUST contain exactly one YAML document.
/conf/ruleset/document-single-yaml-documentA YAML stream with one document, with an explicit document marker, is valid.valid
Covers /req/ruleset/single-document. Fixtures in conformance/ruleset/document-single-yaml-document/.
--- rules : r : given : $.infothen : field : titlefunction : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/ruleset/document-two-yaml-documentsA YAML stream with two documents is invalid.invalid
Covers /req/ruleset/single-document. Fixtures in conformance/ruleset/document-two-yaml-documents/.
rules : {}--- rules : {}
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/req/ruleset/schemarulesetin section The ruleset document
The root value of a ruleset MUST be valid against the ruleset schema of this version of this specification, as JSON Schema 2020-12 JSON-SCHEMA-CORE-2020-12 JSON-SCHEMA-VALIDATION-2020-12 defines validity.
/conf/aliases/
/conf/aliases/name-validAn alias name of letters, digits, underscores, and hyphens starting with a letter is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/aliases/name-valid/.
aliases : Info_v2-x : - $.inforules : r : given : "#Info_v2-x" then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/aliases/scoped-form-validA scoped alias whose every target has formats and given is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/aliases/scoped-form-valid/.
aliases : ParameterObject : targets : -formats : [oas2 ]given : - $.parameters[*] -formats : [oas3 ]given : - $.components.parameters[*]rules : parameter-name : formats : [oas2 ,oas3 ]given : "#ParameterObject" then : field : namefunction : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/aliases/simple-form-validA simple alias that is an array of path expressions is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/aliases/simple-form-valid/.
aliases : Info : - $.info - $.externalDocsrules : r : given : "#Info" then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/aliases/nameAn alias name starting with a digit is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/aliases/name/.
aliases : 1bad : - $.inforules : r : given : "#1bad" then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/aliases/scoped-form-missing-formatsA scoped alias target without formats is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/aliases/scoped-form-missing-formats/.
aliases : Info : targets : -given : - $.inforules : r : given : "#Info" then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/aliases/simple-form-stringA simple alias given as a bare string is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/aliases/simple-form-string/.
aliases : Info : $.inforules : r : given : "#Info" then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/custom-functions/
/conf/custom-functions/declaration-validfunctions as an array of strings with a functionsDir string is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/custom-functions/declaration-valid/.
functions : - isEmptyTitlefunctionsDir : ./checksrules : r : given : $.info.titlethen : function : isEmptyTitle
module. exports= ( v) => ( typeof v=== 'string' && v. trim() !== '' ? [] : [{ message: 'Title is empty.' }]);
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/custom-functions/declaration-not-arrayA functions value that is a string is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/custom-functions/declaration-not-array/.
functions : isEmptyTitlerules : r : given : $.info.titlethen : function : isEmptyTitle
module. exports= ( targetVal) => { if ( typeof targetVal!== 'string' || targetVal. trim() === '' ) { return [{ message: 'Title is empty.' }]; } return []; };
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/extends/
/conf/extends/entry-forms-validA string entry and tuples with each of the three modes are valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/extends/entry-forms-valid/.
extends : - ./base.yaml - [./a.yaml ,recommended ] - [./b.yaml ,all ] - [./c.yaml ,"off" ]
rules : base-rule : given : $then : function : falsy
rules : a-rule : given : $then : function : falsy
rules : b-rule : given : $then : function : falsy
rules : c-rule : given : $then : function : falsy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/extends/entry-tuple-lengthAn extends tuple with a third element is valid; elements after the mode are ignored.valid
Covers /req/ruleset/schema. Fixtures in conformance/extends/entry-tuple-length/.
extends : - [./base.yaml ,all ,extra ]
rules : base-rule : given : $then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/extends/shape-arrayA ruleset whose extends is an array mixing a string and a tuple is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/extends/shape-array/.
extends : - ./base.yaml - [./other.yaml ,all ]
rules : base-rule : given : $then : function : truthy
rules : other-rule : given : $then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/extends/shape-stringA ruleset whose extends is a single string is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/extends/shape-string/.
extends : ./base.yaml
rules : base-rule : given : $then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/extends/entry-invalid-modeAn extends tuple with the mode strict is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/extends/entry-invalid-mode/.
extends : - [./base.yaml ,strict ]
rules : base-rule : given : $then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/extends/shape-objectA ruleset whose extends is an object is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/extends/shape-object/.
extends : ruleset : ./base.yaml
rules : base-rule : given : $then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/formats/
/conf/formats/shape-validformats as an array of strings on the ruleset, a rule, an override, and an alias target is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/formats/shape-valid/.
formats : [oas3 ]aliases : Params : targets : -formats : [oas3 ]given : - $.components.parameters[*]rules : r : formats : [oas3_1 ]given : "#Params" then : function : truthyoverrides : -files : ["legacy/**" ]formats : [oas3_0 ]rules : r : warn
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/formats/shape-stringA ruleset whose formats is a string is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/formats/shape-string/.
formats : oas3rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/formats/unknown-identifierA rule whose formats names an identifier outside the table, oas31, is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/formats/unknown-identifier/.
rules : r : formats : [oas31 ]given : $.infothen : field : titlefunction : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/given/
/conf/given/path-expression-formsA JSONPath expression starting with $ and an alias reference starting with # are both valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/given/path-expression-forms/.
aliases : Info : - $.inforules : r : given : - $.paths -"#Info" then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/given/presentA rule with given and then is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/given/present/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/given/shape-validgiven as a single path expression and as an array of them are both valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/given/shape-valid/.
rules : one : given : $.infothen : function : truthymany : given : - $.info.title - $.info.descriptionthen : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/given/bare-property-nameA rule whose given is a bare property name is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/given/bare-property-name/.
rules : r : given : pathsthen : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/given/requiredA rule with then but no given is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/given/required/.
rules : r : then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/given/shape-empty-arrayA rule with given : [] is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/given/shape-empty-array/.
rules : r : given : []then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/given/shape-objectA rule whose given is an object is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/given/shape-object/.
rules : r : given : path : $.infothen : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/overrides/
/conf/overrides/file-scoped-validA file-scoped override with rules, extends, formats, parserOptions, and aliases is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/overrides/file-scoped-valid/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthyoverrides : -files : ["legacy/**" ]extends : ./base.yamlformats : [oas3 ]parserOptions : duplicateKeys : warnaliases : Info : - $.inforules : acme-operation-summary : warn
rules : base-rule : given : $then : function : falsy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/overrides/mixed-globsAn override whose files mix a pointer glob and a plain glob is file-scoped, so it can define rules with shorthands and is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/overrides/mixed-globs/.
rules : r : given : $then : function : falsyoverrides : -files : ["api.yaml#/paths" ,"**/*.yaml" ]rules : r : hint
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/overrides/pointer-scoped-validA pointer-scoped override whose rules are severities only is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/overrides/pointer-scoped-valid/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthyoverrides : -files : ["document.yaml#/paths/~1legacy" ]rules : acme-operation-summary : "off"
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/overrides/shape-validAn override with a non-empty files array is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/overrides/shape-valid/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthyoverrides : -files : ["legacy/**" ]rules : acme-operation-summary : warn
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/overrides/file-scoped-emptyA file-scoped override with neither extends nor rules is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/overrides/file-scoped-empty/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthyoverrides : -files : ["**/*.yaml" ]
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/overrides/pointer-scoped-extendsA pointer-scoped override with extends is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/overrides/pointer-scoped-extends/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthyoverrides : -files : ["document.yaml#/paths" ]extends : ./base.yamlrules : acme-operation-summary : error
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/overrides/pointer-scoped-rule-objectA pointer-scoped override that defines a rule object is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/overrides/pointer-scoped-rule-object/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthyoverrides : -files : ["document.yaml#/paths" ]rules : acme-operation-summary : given : $then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/overrides/shape-emptyAn empty overrides array is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/overrides/shape-empty/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthyoverrides : []
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/overrides/shape-no-filesAn override without files is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/overrides/shape-no-files/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthyoverrides : -rules : acme-operation-summary : error
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/parser-options/
/conf/parser-options/shape-validBoth parser options with severity values are valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/parser-options/shape-valid/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthyparserOptions : duplicateKeys : warnincompatibleValues : hint
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/parser-options/shape-extension-propertyAn extension property inside parserOptions is invalid: the permission covers the root and rules only, and parserOptions admits no other properties.invalid
Covers /req/ruleset/schema, /per/extension-properties/anywhere. Fixtures in conformance/parser-options/shape-extension-property/.
rules : r : given : $then : function : truthyparserOptions : x-note : not permitted here
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/parser-options/shape-unknown-optionAn unknown parser option is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/parser-options/shape-unknown-option/.
rules : quiet : given : $.nothingthen : function : truthyparserOptions : trailingCommas : error
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/parser-options/shape-unknown-severityduplicateKeys : critical is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/parser-options/shape-unknown-severity/.
rules : quiet : given : $.nothingthen : function : truthyparserOptions : duplicateKeys : critical
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/rule/
/conf/rule/all-propertiesA rule using every property with its documented type is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/rule/all-properties/.
rules : acme-operation-summary : description : Every operation needs a summary.message : "{{description}}" severity : errorformats : [oas3 ]documentationUrl : https://engineering.acme.com/api/governance#summaryrecommended : trueresolved : truetags : [style ,docs ]type : styleextensions : owner : platform-teamx-jira : GOV-142given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/rule/then-presentA rule with then and given is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/rule/then-present/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/rule/recommended-not-booleanA rule whose recommended is a string is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/rule/recommended-not-boolean/.
rules : r : recommended : "yes" given : $then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/rule/then-requiredA rule with given but no then is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/rule/then-required/.
rules : r : given : $
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/rule/type-unknownA rule whose type is neither style nor validation is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/rule/type-unknown/.
rules : r : type : lintgiven : $then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/rule/unknown-propertyA rule with the unknown property severty is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/rule/unknown-property/.
rules : r : severty : errorgiven : $then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/ruleset/
/conf/ruleset/all-propertiesA ruleset using every root property with its documented type is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/ruleset/all-properties/.
description : Acme API style guidedocumentationUrl : https://engineering.acme.com/api/governanceformats : [oas3 ]extends : ./base.yamlaliases : Info : - $.infooverrides : -files : ["legacy/**" ]rules : base-rule : warnparserOptions : duplicateKeys : warnfunctions : - isEmptyTitlefunctionsDir : ./checksrules : title-filled : given : $.info.titlethen : function : isEmptyTitle
rules : base-rule : given : $then : function : falsy
module. exports= ( v) => ( typeof v=== 'string' && v. trim() !== '' ? [] : [{ message: 'Title is empty.' }]);
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/ruleset/contributes-extends-onlyA ruleset with only extends is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/ruleset/contributes-extends-only/.
extends : ./base.yaml
rules : base-rule : given : $then : function : falsy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/ruleset/extension-root-propertyA ruleset with the extension property x-owner at the root is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/ruleset/extension-root-property/.
x-owner : platform-teamrules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/ruleset/contributes-emptyA ruleset with only description is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/ruleset/contributes-empty/.
description : Says nothing.
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/ruleset/description-not-stringA ruleset whose description is a number is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/ruleset/description-not-string/.
description : 1rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/ruleset/document-root-arrayA ruleset whose root value is an array is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/ruleset/document-root-array/.
-rules : {}
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/ruleset/documentation-url-intranetA ruleset whose documentationUrl names a single-label intranet host, http://wiki/docs, is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/ruleset/documentation-url-intranet/.
documentationUrl : http://wiki/docsrules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/ruleset/documentation-url-invalidA ruleset whose documentationUrl is not a URI is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/ruleset/documentation-url-invalid/.
documentationUrl : "governance docs" rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/ruleset/documentation-url-mailtoA ruleset whose documentationUrl is a mailto : URI is invalid: only http, https, and ftp URLs are accepted.invalid
Covers /req/ruleset/schema. Fixtures in conformance/ruleset/documentation-url-mailto/.
documentationUrl : "mailto:api-governance@acme.example" rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/ruleset/rules-not-objectA ruleset whose rules is an array is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/ruleset/rules-not-object/.
rules : - r
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/ruleset/unknown-root-propertyA ruleset with rules and the unknown root property rule is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/ruleset/unknown-root-property/.
rules : r : given : $then : function : truthyrule : s : given : $then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/severity/
/conf/severity/values-nameA rule with severity : hint is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/severity/values-name/.
rules : r : severity : hintgiven : $then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/severity/values-numberA rule with severity : 3 is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/severity/values-number/.
rules : r : severity : 3given : $then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/severity/values-invalidA rule with severity : critical is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/severity/values-invalid/.
rules : r : severity : criticalgiven : $then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/shorthands/
/conf/shorthands/values-validEvery severity name and both booleans are valid in the shorthand position.valid
Covers /req/ruleset/schema. Fixtures in conformance/shorthands/values-valid/.
extends : ./a.yamlrules : r : hints : false
rules : r : given : $then : function : falsys : given : $then : function : falsy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/shorthands/numericA numeric severity in the shorthand position is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/shorthands/numeric/.
extends : ./a.yamlrules : r : 1
rules : r : recommended : falseseverity : warngiven : $then : function : falsy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/shorthands/unknown-nameAn unknown name in the shorthand position is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/shorthands/unknown-name/.
extends : ./a.yamlrules : r : critical
rules : r : recommended : falseseverity : warngiven : $then : function : falsy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/then/
/conf/then/invocation-form-validAn invocation with function, field, and functionOptions of the right types is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/then/invocation-form-valid/.
rules : r : given : $.infothen : field : titlefunction : lengthfunctionOptions : max : 120
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/then/shape-empty-arrayA rule with then : [] is valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/then/shape-empty-array/.
rules : r : given : $then : []
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/then/shape-validthen as a single invocation and as an array of them are both valid.valid
Covers /req/ruleset/schema. Fixtures in conformance/then/shape-valid/.
rules : one : given : $.infothen : function : truthymany : given : $.infothen : -function : truthy -field : titlefunction : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/then/function-missingAn invocation without function is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/then/function-missing/.
rules : r : given : $then : field : title
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/then/options-not-objectAn invocation whose functionOptions is a string is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/then/options-not-object/.
rules : r : given : $then : function : patternfunctionOptions : "^a"
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/then/shape-stringA rule whose then is a string is invalid.invalid
Covers /req/ruleset/schema. Fixtures in conformance/then/shape-string/.
rules : r : given : $then : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/req/shorthands/inherited-onlyrulesetin section Shorthands
A shorthand in the root rules MUST name a rule the ruleset inherits through extends.
/conf/shorthands/inheritedA shorthand for a rule inherited through extends is valid.valid
Covers /req/shorthands/inherited-only. Fixtures in conformance/shorthands/inherited/.
extends : ./a.yamlrules : r : error
rules : r : given : $then : function : falsys : given : $then : function : falsy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/shorthands/not-inheritedA shorthand for a rule the ruleset does not inherit is invalid.invalid
Covers /req/shorthands/inherited-only. Fixtures in conformance/shorthands/not-inherited/.
rules : r : error
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/req/aliases/no-cyclerulesetin section aliases
An alias MUST NOT refer to itself, directly or through other aliases.
/conf/aliases/chainAn alias defined through another alias, without a cycle, is valid.valid
Covers /req/aliases/no-cycle. Fixtures in conformance/aliases/chain/.
aliases : Info : - $.infoInfoTitle : -"#Info.title" rules : r : given : "#InfoTitle" then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/aliases/cycleTwo aliases that refer to each other are invalid.invalid
Covers /req/aliases/no-cycle. Fixtures in conformance/aliases/cycle/.
aliases : A : -"#B" B : -"#A" rules : r : given : "#A" then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/req/given/alias-definedrulesetin section given
An alias reference in a rule MUST name an alias declared under aliases in the ruleset whose rules contain the rule or, for a rule in a file-scoped override, in that override or that ruleset. When the rule redefines a rule inherited through extends, the ruleset that defines the inherited rule MUST declare that alias too.
/conf/given/alias-declaredA rule referencing an alias the ruleset declares is valid.valid
Covers /req/given/alias-defined. Fixtures in conformance/given/alias-declared/.
aliases : Info : - $.inforules : r : given : "#Info.title" then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/given/alias-redefined-bothA rule that redefines an inherited rule with an alias both rulesets declare is valid.valid
Covers /req/given/alias-defined. Fixtures in conformance/given/alias-redefined-both/.
extends : ./base.yamlaliases : X : - $.inforules : base-rule : given : "#X" then : field : titlefunction : truthy
aliases : X : - $.inforules : base-rule : given : $.infothen : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/given/alias-redefined-ownA rule that redefines an inherited rule with an alias only the extending ruleset declares is invalid.invalid
Covers /req/given/alias-defined. Fixtures in conformance/given/alias-redefined-own/.
extends : ./base.yamlaliases : Y : - $.inforules : base-rule : given : "#Y" then : field : titlefunction : truthy
rules : base-rule : given : $.infothen : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/given/alias-undefinedA rule referencing an alias the ruleset does not declare is invalid.invalid
Covers /req/given/alias-defined. Fixtures in conformance/given/alias-undefined/.
rules : r : given : "#Missing" then : function : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/req/then/function-knownrulesetin section then
function MUST name a #core-functions|core function or a custom function declared in functions.
/conf/then/function-coreA rule naming a core function is valid.valid
Covers /req/then/function-known. Fixtures in conformance/then/function-core/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/then/function-unknownA rule naming a function that is neither core nor declared is invalid.invalid
Covers /req/then/function-known. Fixtures in conformance/then/function-unknown/.
rules : r : given : $then : function : nonexistent
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/req/then/function-options-validrulesetin section then
When function names a core function, functionOptions MUST satisfy that function’s options in #core-functions: only the options its table lists, with the types listed, every required option present, and no functionOptions at all for a function that takes no options.
/conf/then/options-validCore functions with options that match their tables are valid.valid
Covers /req/then/function-options-valid. Fixtures in conformance/then/options-valid/.
rules : a : given : $.namethen : function : casingfunctionOptions : type : cameldisallowDigits : trueb : given : $.tagsthen : function : alphabeticalc : given : $.namethen : function : lengthfunctionOptions : min : 1
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/then/options-on-no-option-functiontruthy with options is invalid, because it takes none.invalid
Covers /req/then/function-options-valid. Fixtures in conformance/then/options-on-no-option-function/.
rules : r : given : $.namethen : function : truthyfunctionOptions : strict : true
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/then/options-required-missinglength with empty options is invalid, because it needs min or max.invalid
Covers /req/then/function-options-valid. Fixtures in conformance/then/options-required-missing/.
rules : r : given : $.namethen : function : lengthfunctionOptions : {}
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/conf/then/options-unknown-valuecasing with type : lower, which is not a case the function knows, is invalid.invalid
Covers /req/then/function-options-valid. Fixtures in conformance/then/options-unknown-value/.
rules : r : given : $.namethen : function : casingfunctionOptions : type : lower
Procedure
Load the ruleset with a linter.
Expected
The ruleset is invalid: a linter rejects it.
/per/extension-properties/anywhererulesetin section Extension properties
A ruleset MAY have extension properties at its root and on any rule, with any value.
/conf/extension-properties/acceptedA ruleset with extension properties of several types at the root and on a rule is accepted.valid
Covers /per/extension-properties/anywhere. Fixtures in conformance/extension-properties/accepted/.
x-owner : platform-teamx-review : last : 2026-10-01ticket : GOV-142rules : acme-operation-summary : x-jira : GOV-142x-rationale : Summaries feed the generated docs.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
Procedure
Load the ruleset with a linter.
Expected
The ruleset is valid: a linter loads it.
/conf/parser-options/shape-extension-propertyAn extension property inside parserOptions is invalid: the permission covers the root and rules only, and parserOptions admits no other properties.invalidalso covers /req/ruleset/schema; shown there
Conformance class /conf/linter-core
/req/document/any-rootlinterin section The document model
A linter MUST lint a document whose root value is not an object, and $ MUST select that root value.
/conf/document/empty-fileAn empty file is the document null, and a rule on $ sees it.1 result
Covers /req/document/any-root. Fixtures in conformance/document/empty-file/.
rules : r : given : $then : function : truthy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [] } ]
/conf/document/root-arrayA document whose root is an array is linted, and $ selects the array.1 result
Covers /req/document/any-root. Fixtures in conformance/document/root-array/.
rules : r : given : $then : function : falsy
[1 ,2 ]
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [] } ]
/req/document/encodinglinterin section The document model
A linter MUST read a document and a ruleset as UTF-8.
/conf/document/utf8-keysA document is read as UTF-8, so non-ASCII keys arrive intact in path.2 results
Covers /req/document/encoding. Fixtures in conformance/document/utf8-keys/.
rules : r : given : $then : field : "é" function : truthys : given : $then : field : "😀" function : truthy
é : "" 😀 : ""
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "\u00e9" ] }, { "code" : "s" , "path" : [ "\ud83d\ude00" ] } ]
/req/parsing/syntax-errorlinterin section Parsing
When a document has a syntax error, a linter MUST produce a result with the code parser and the severity error.
/conf/parsing/syntax-errorA YAML document with an unclosed flow sequence is reported as a parser error.1 result
Covers /req/parsing/syntax-error. Fixtures in conformance/parsing/syntax-error/.
rules : r : given : $.nothingthen : function : truthy
a : [unclosed
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "parser" , "severity" : 0 } ]
/req/parsing/several-documentslinterin section Parsing
When a YAML document contains several documents, a linter MUST produce a result with the code parser and the severity error, and MUST lint the first document.
/conf/parsing/several-documentsA YAML file with two documents gets a parser error, and the rules run on the first document.2 results
Covers /req/parsing/several-documents. Fixtures in conformance/parsing/several-documents/.
rules : r : given : $then : function : falsy
a : 1--- b : 2
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "parser" , "severity" : 0 }, { "code" : "r" , "path" : [] } ]
/req/parser-options/duplicate-keyslinterin section Parsing
When a document has a duplicate key, a linter MUST produce a result with the code parser and the severity of duplicateKeys, error when it is not set, and MUST NOT produce one when it is off.
/conf/parser-options/duplicate-keys-defaultA duplicate key is reported as an error by default.1 result
Covers /req/parser-options/duplicate-keys. Fixtures in conformance/parser-options/duplicate-keys-default/.
rules : quiet : given : $.nothingthen : function : truthy
info : title : Onetitle : Two
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "parser" , "severity" : 0 } ]
/conf/parser-options/duplicate-keys-warnWith duplicateKeys : warn, a duplicate key is reported as a warning.1 result
Covers /req/parser-options/duplicate-keys. Fixtures in conformance/parser-options/duplicate-keys-warn/.
rules : quiet : given : $.nothingthen : function : truthyparserOptions : duplicateKeys : warn
info : title : Onetitle : Two
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "parser" , "severity" : 1 } ]
/conf/parser-options/duplicate-keys-offWith duplicateKeys : off, a duplicate key is not reported.0 results
Covers /req/parser-options/duplicate-keys. Fixtures in conformance/parser-options/duplicate-keys-off/.
rules : quiet : given : $.nothingthen : function : truthyparserOptions : duplicateKeys : off
info : title : Onetitle : Two
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces no results.
/req/parser-options/incompatible-valueslinterin section Parsing
When a YAML document has a mapping key that is not a string scalar, such as 1, true, or null, a linter MUST produce a result with the code parser and the severity of incompatibleValues, error when it is not set, and MUST NOT produce one when it is off.
/conf/parser-options/incompatible-valuesA YAML mapping key that is a number, not a string, has no JSON representation and is reported as an error by default.1 result
Covers /req/parser-options/incompatible-values. Fixtures in conformance/parser-options/incompatible-values/.
rules : quiet : given : $.nothingthen : function : truthy
1 : x
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "parser" , "severity" : 0 } ]
/req/resolution/locallinterin section Reference resolution
A linter MUST replace a reference whose $ref is a fragment with the value the JSON Pointer names in the same document.
/conf/resolution/localA $ref to a pointer in the same document is replaced by the referenced schema, whose type the rule then sees.1 result
Covers /req/resolution/local. Fixtures in conformance/resolution/local/.
rules : id-is-string : given : $.components.schemas.Pet.properties.id.typethen : function : enumerationfunctionOptions : values : [string ]
components : schemas : Id : type : integerPet : properties : id : $ref : "#/components/schemas/Id"
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "id-is-string" } ]
/req/resolution/externallinterin section Reference resolution
A linter MUST replace a reference whose $ref names another document with the value its fragment names in that document, after resolving the $ref against the location of the referencing document.
/conf/resolution/externalA $ref to another file, relative to the document, is replaced by the referenced schema.1 result
Covers /req/resolution/external. Fixtures in conformance/resolution/external/.
rules : id-is-string : given : $.components.schemas.Pet.properties.id.typethen : function : enumerationfunctionOptions : values : [string ]
components : schemas : Pet : properties : id : $ref : "./schemas.yaml#/Id"
Id : type : integer
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "id-is-string" } ]
/req/resolution/cyclelinterin section Reference resolution
A linter MUST resolve a document whose references form a cycle without failing, and MUST evaluate rules against it.
/conf/resolution/cycleA schema that references itself resolves without error, and a rule sees its type.1 result
Covers /req/resolution/cycle. Fixtures in conformance/resolution/cycle/.
rules : node-is-string : given : $.components.schemas.Node.typethen : function : enumerationfunctionOptions : values : [string ]
components : schemas : Node : type : objectproperties : children : type : arrayitems : $ref : "#/components/schemas/Node"
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "node-is-string" } ]
/req/resolution/unresolvablelinterin section Reference resolution
When a $ref cannot be resolved, a linter MUST produce a result with the code invalid-ref and the severity error, and MUST leave the reference object in place for the rules.
/conf/resolution/unresolvableA $ref to a file that does not exist is reported as invalid-ref at severity error.1 result
Covers /req/resolution/unresolvable. Fixtures in conformance/resolution/unresolvable/.
rules : quiet : given : $.nothingthen : function : truthy
components : schemas : Pet : $ref : "./missing.yaml#/Pet"
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "invalid-ref" , "severity" : 0 } ]
/conf/resolution/unresolvable-keptAfter a $ref fails to resolve, a rule still sees the reference object.2 results
Covers /req/resolution/unresolvable. Fixtures in conformance/resolution/unresolvable-kept/.
rules : ref-kept : given : $.components.schemas.Petthen : field : $reffunction : undefined
components : schemas : Pet : $ref : "./missing.yaml#/Pet"
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "invalid-ref" , "severity" : 0 }, { "code" : "ref-kept" , "path" : [ "components" , "schemas" , "Pet" , "$ref" ] } ]
/req/linter/reject-invalidlinterin section Loading a ruleset
A linter MUST reject a ruleset that violates any requirement whose conformance target is ruleset, or that extends, directly or through another ruleset’s extends, a ruleset that violates one, and MUST NOT produce results from it.
/conf/linter/accept-validA valid ruleset is loaded and linting produces its results.2 results
Covers /req/linter/reject-invalid, /req/linter/accept-valid. Fixtures in conformance/linter/accept-valid/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "acme-operation-summary" }, { "code" : "acme-operation-summary" } ]
/conf/extends/base-invalidA ruleset that extends a ruleset with an invalid given is rejected.rejected
Covers /req/linter/reject-invalid. Fixtures in conformance/extends/base-invalid/.
extends : ./base.yaml
rules : r : given : pathsthen : function : truthy
Procedure
Lint an empty document with ruleset.yaml and collect the results as JSON.
Expected
The linter rejects the ruleset and produces no results.
/conf/linter/reject-invalidGiven an invalid ruleset, the linter rejects it and produces no results.rejected
Covers /req/linter/reject-invalid. Fixtures in conformance/linter/reject-invalid/.
-rules : {}
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
The linter rejects the ruleset and produces no results.
/req/linter/accept-validlinterin section Loading a ruleset
A linter MUST load a ruleset that satisfies every requirement whose conformance target is ruleset, when every ruleset it extends and every custom function it names can be loaded, unless /req/overrides/checked-when-applied has it rejected for the document being linted.
/conf/linter/accept-validA valid ruleset is loaded and linting produces its results.2 resultsalso covers /req/linter/reject-invalid; shown there
/req/extends/loadlinterin section Loading a ruleset
A linter MUST load every ruleset identified in extends and make each of its rules available to the extending ruleset under its rule name.
/conf/extends/loadA rule defined only in the extended ruleset is available to the extending one by name.2 results
Covers /req/extends/load. Fixtures in conformance/extends/load/.
extends : ./base.yamlrules : acme-operation-summary : error
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "acme-operation-summary" }, { "code" : "acme-operation-summary" } ]
/conf/linter/walkthroughThe running example of the Linting chapter: a ruleset that extends a base, enables a rule by shorthand, declares an alias, and overrides a severity for legacy documents, linting one legacy document.3 results
Covers /req/extends/load, /req/overrides/scope, /req/shorthands/true, /req/formats/ruleset-default, /req/given/alias-expansion, /req/result/severity. Fixtures in conformance/linter/walkthrough/.
extends : ./acme-base.yamlformats : [oas3 ]aliases : Operation : -"$.paths[*]['get','put','post','delete']" rules : operation-id-camel-case : trueoperation-has-tags : description : Every operation is tagged.severity : hintgiven : "#Operation" then : field : tagsfunction : definedoverrides : -files : ["legacy/**" ]rules : operation-summary : error
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : operationId : list_petsresponses : "200" :description : OKpost : summary : Add a petoperationId : addPettags : [pets ]responses : "201" :description : Created
rules : operation-summary : description : Every operation needs a summary.severity : warngiven : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthyoperation-id-camel-case : description : operationId is written in camelCase.recommended : falsegiven : $.paths[*]['get','put','post','delete','options','head','patch','trace'].operationIdthen : function : casingfunctionOptions : type : camel
Procedure
Lint legacy/petstore.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 3 results, matching on the fields given, in any order:
[ { "code" : "operation-summary" , "path" : [ "paths" , "/pets" , "get" ], "severity" : 0 }, { "code" : "operation-has-tags" , "path" : [ "paths" , "/pets" , "get" ], "severity" : 3 }, { "code" : "operation-id-camel-case" , "path" : [ "paths" , "/pets" , "get" , "operationId" ], "severity" : 1 } ]
/req/extends/unresolvablelinterin section Loading a ruleset
A linter MUST reject a ruleset whose extends identifies a ruleset it cannot load.
/conf/extends/resolvableA ruleset extending a file that exists is loaded and the inherited rule runs.1 result
Covers /req/extends/unresolvable. Fixtures in conformance/extends/resolvable/.
extends : ./base.yaml
{}
rules : base-rule : given : $then : function : falsy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "base-rule" } ]
/conf/extends/unresolvableA ruleset extending a file that does not exist is rejected.rejected
Covers /req/extends/unresolvable. Fixtures in conformance/extends/unresolvable/.
extends : ./does-not-exist.yaml
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
The linter rejects the ruleset and produces no results.
/req/extension-properties/ignoredlinterin section Loading a ruleset
A linter MUST NOT let an extension property change which results it produces.
/conf/extension-properties/ignoredExtension properties at the root and on a rule change nothing about the results.2 results
Covers /req/extension-properties/ignored. Fixtures in conformance/extension-properties/ignored/.
x-owner : platform-teamrules : acme-operation-summary : x-jira : GOV-142description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "acme-operation-summary" }, { "code" : "acme-operation-summary" } ]
/req/overrides/scopelinterin section Applying overrides
A linter MUST apply an override to a document only when the document’s path, relative to the directory of the ruleset file and with / as the separator, matches one of the override’s files globs.
/conf/linter/walkthroughThe running example of the Linting chapter: a ruleset that extends a base, enables a rule by shorthand, declares an alias, and overrides a severity for legacy documents, linting one legacy document.3 resultsalso covers /req/extends/load, /req/shorthands/true, /req/formats/ruleset-default, /req/given/alias-expansion, /req/result/severity; shown there
/conf/overrides/scope-matchA document inside the override’s globs gets the override’s severity.2 results
Covers /req/overrides/scope. Fixtures in conformance/overrides/scope-match/.
rules : acme-operation-summary : description : Every operation needs a summary.severity : warngiven : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthyoverrides : -files : ["legacy/**" ]rules : acme-operation-summary : error
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint legacy/document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "severity" : 0 }, { "severity" : 0 } ]
/conf/overrides/scope-no-matchA document outside the override’s globs keeps the base severity.2 results
Covers /req/overrides/scope. Fixtures in conformance/overrides/scope-no-match/.
rules : acme-operation-summary : description : Every operation needs a summary.severity : warngiven : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthyoverrides : -files : ["legacy/**" ]rules : acme-operation-summary : error
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "severity" : 1 }, { "severity" : 1 } ]
/conf/overrides/scope-outside-ruleset-directoryThe same glob does not match a document in a legacy/ directory that is not next to the ruleset, whatever the working directory.1 result
Covers /req/overrides/scope. Fixtures in conformance/overrides/scope-outside-ruleset-directory/.
rules : r : given : $.infothen : field : titlefunction : truthyoverrides : -files : ['legacy/**' ]rules : r : error
info : {}
Procedure
Lint legacy/a.yaml with rules/ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "severity" : 1 } ]
/conf/overrides/scope-ruleset-directoryA glob is matched against the document’s path relative to the ruleset file’s directory, so legacy/** matches a document in legacy/ next to the ruleset.1 result
Covers /req/overrides/scope. Fixtures in conformance/overrides/scope-ruleset-directory/.
rules : r : given : $.infothen : field : titlefunction : truthyoverrides : -files : ['legacy/**' ]rules : r : error
info : {}
Procedure
Lint rules/legacy/a.yaml with rules/ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "severity" : 0 } ]
/req/overrides/orderlinterin section Applying overrides
When several overrides match a document, a linter MUST apply them in array order, each on top of the previous, and all of them on top of the ruleset itself.
/conf/overrides/orderOf two overrides that both match, the later one wins.2 results
Covers /req/overrides/order. Fixtures in conformance/overrides/order/.
rules : acme-operation-summary : description : Every operation needs a summary.severity : warngiven : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthyoverrides : -files : ["**/*.yaml" ]rules : acme-operation-summary : error -files : ["**/*.yaml" ]rules : acme-operation-summary : hint
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "severity" : 3 }, { "severity" : 3 } ]
/req/overrides/extendslinterin section Applying overrides
For a document a file-scoped override applies to, a linter MUST load the rulesets the override’s extends identifies and make their rules available in addition to those of the rulesets the ruleset extends, or, when the override has no rules, in place of them.
/conf/overrides/extendsAn override’s extends adds rules for the documents it matches.1 result
Covers /req/overrides/extends. Fixtures in conformance/overrides/extends/.
rules : base-rule : given : $.nothingthen : function : truthyoverrides : -files : ["**/*.yaml" ]extends : ./extra.yaml
{}
rules : extra-rule : given : $then : function : falsy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "extra-rule" } ]
/conf/overrides/extends-with-rulesAn applying override with extends and rules adds to the rulesets the ruleset extends: a, b, and r all run.3 results
Covers /req/overrides/extends. Fixtures in conformance/overrides/extends-with-rules/.
extends : ./a.yamlrules : r : given : $.infothen : field : titlefunction : truthyoverrides : -files : ["**/*.yaml" ]extends : ./b.yamlrules : r : error
openapi : 3.1.0info : version : "1"
rules : a : given : $.infothen : field : titlefunction : truthy
rules : b : given : $.infothen : field : titlefunction : truthy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 3 results, matching on the fields given, in any order:
[ { "code" : "a" }, { "code" : "b" }, { "code" : "r" , "severity" : 0 } ]
/conf/overrides/extends-without-rulesAn applying override with extends and no rules replaces the rulesets the ruleset extends: b and the ruleset’s own r run, a does not.2 results
Covers /req/overrides/extends. Fixtures in conformance/overrides/extends-without-rules/.
extends : ./a.yamlrules : r : given : $.infothen : field : titlefunction : truthyoverrides : -files : ["**/*.yaml" ]extends : ./b.yaml
openapi : 3.1.0info : version : "1"
rules : a : given : $.infothen : field : titlefunction : truthy
rules : b : given : $.infothen : field : titlefunction : truthy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "b" }, { "code" : "r" } ]
/req/overrides/aliaseslinterin section Applying overrides
For a document a file-scoped override applies to, a linter MUST resolve the alias references of each rule the override defines and the ruleset does not against the override’s aliases and the ruleset’s, the override’s alias taking precedence for a name both declare, and MUST resolve the alias references of every other rule as if the override declared no aliases.
/conf/overrides/aliases-existing-ruleAn alias an override declares does not change a rule the ruleset defines: the rule keeps the ruleset’s alias and only its severity changes.1 result
Covers /req/overrides/aliases. Fixtures in conformance/overrides/aliases-existing-rule/.
aliases : Info : - $..inforules : info-description : given : "#Info.description" then : function : truthyoverrides : -files : ["**/*.yaml" ]aliases : Info : - $.nothingrules : info-description : error
info : title : Petstoredescription : ""
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "info-description" , "severity" : 0 } ]
/conf/overrides/aliases-new-ruleA rule an override defines resolves aliases against the override’s, which take precedence over the ruleset’s alias of the same name.1 result
Covers /req/overrides/aliases. Fixtures in conformance/overrides/aliases-new-rule/.
aliases : Info : - $.nothingrules : base : given : $.infothen : function : truthyoverrides : -files : ["**/*.yaml" ]aliases : Info : - $..inforules : override-rule : given : "#Info.description" then : function : truthy
info : title : Petstoredescription : ""
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "override-rule" , "path" : [ "info" , "description" ] } ]
/conf/overrides/aliases-outside-globsOutside the override’s globs its aliases have no effect, and the rule keeps its default severity.1 result
Covers /req/overrides/aliases. Fixtures in conformance/overrides/aliases-outside-globs/.
aliases : Info : - $..inforules : info-description : given : "#Info.description" then : function : truthyoverrides : -files : ["legacy/**" ]aliases : Info : - $.nothingrules : info-description : error
info : title : Petstoredescription : ""
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "info-description" , "severity" : 1 } ]
/req/overrides/reject-foreign-propertylinterin section Applying overrides
For a document a file-scoped override applies to, a linter MUST reject the ruleset when the override has a property the Ruleset Object does not have.
/conf/overrides/unknown-property-not-appliedThe same override, applying to no document being linted, does not stop the ruleset from loading.1 result
Covers /req/overrides/reject-foreign-property, /req/overrides/checked-when-applied. Fixtures in conformance/overrides/unknown-property-not-applied/.
rules : r : given : $then : function : falsyoverrides : -files : ["legacy/**" ]foo : barrules : r : error
{}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "severity" : 1 } ]
/conf/overrides/unknown-property-appliedAn override with a property the Ruleset Object does not have makes a linter reject the ruleset for a document it applies to.rejected
Covers /req/overrides/reject-foreign-property. Fixtures in conformance/overrides/unknown-property-applied/.
rules : r : given : $then : function : falsyoverrides : -files : ["**/*.yaml" ]foo : barrules : r : error
{}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
The linter rejects the ruleset and produces no results.
/req/overrides/reject-unknown-shorthandlinterin section Applying overrides
For a document a file-scoped override applies to, a linter MUST reject the ruleset when a shorthand in the override’s rules names a rule the ruleset neither defines nor inherits.
/conf/shorthands/override-own-ruleA shorthand in an applying override for a rule the ruleset defines itself sets its severity.1 result
Covers /req/overrides/reject-unknown-shorthand. Fixtures in conformance/shorthands/override-own-rule/.
rules : r : given : $then : function : falsyoverrides : -files : ["**/*.yaml" ]rules : r : error
{}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "severity" : 0 } ]
/conf/shorthands/override-unknown-not-appliedA shorthand for an unknown rule in an override that applies to no document being linted does not stop the ruleset from loading.1 result
Covers /req/overrides/reject-unknown-shorthand, /req/overrides/checked-when-applied. Fixtures in conformance/shorthands/override-unknown-not-applied/.
rules : r : given : $then : function : falsyoverrides : -files : ["legacy/**" ]rules : nope : error
{}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "severity" : 1 } ]
/conf/shorthands/override-unknownA shorthand in an override for a rule the ruleset neither defines nor inherits makes a linter reject the ruleset for a document the override applies to.rejected
Covers /req/overrides/reject-unknown-shorthand. Fixtures in conformance/shorthands/override-unknown/.
rules : r : given : $then : function : falsyoverrides : -files : ["**/*.yaml" ]rules : nope : error
{}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
The linter rejects the ruleset and produces no results.
/req/overrides/reject-undeclared-aliaslinterin section Applying overrides
For a document a file-scoped override applies to, a linter MUST reject the ruleset when a rule the override redefines has an alias reference to an alias that the ruleset defining the rule does not declare.
/conf/overrides/redefined-rule-alias-declaredAn override that redefines a rule with an alias the ruleset declares is loaded, and the redefined rule runs.1 result
Covers /req/overrides/reject-undeclared-alias. Fixtures in conformance/overrides/redefined-rule-alias-declared/.
aliases : B : - $.inforules : r : given : $.infothen : field : titlefunction : truthyoverrides : -files : ["**/*.yaml" ]rules : r : given : "#B" then : field : titlefunction : truthy
info : description : no title
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "info" ] } ]
/conf/overrides/redefined-rule-alias-appliedAn override that redefines a rule with an alias only the override declares makes a linter reject the ruleset for a document it applies to.rejected
Covers /req/overrides/reject-undeclared-alias. Fixtures in conformance/overrides/redefined-rule-alias-applied/.
rules : r : given : $.infothen : field : titlefunction : truthyoverrides : -files : ["**/*.yaml" ]aliases : B : - $.inforules : r : given : "#B" then : function : truthy
info : version : "1"
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
The linter rejects the ruleset and produces no results.
/req/overrides/checked-when-appliedlinterin section Applying overrides
A linter MUST NOT reject a ruleset for the reasons in the three requirements above when the override applies to no document being linted.
/conf/overrides/unknown-property-not-appliedThe same override, applying to no document being linted, does not stop the ruleset from loading.1 resultalso covers /req/overrides/reject-foreign-property; shown there
/conf/shorthands/override-unknown-not-appliedA shorthand for an unknown rule in an override that applies to no document being linted does not stop the ruleset from loading.1 resultalso covers /req/overrides/reject-unknown-shorthand; shown there
/req/overrides/pointer-scopelinterin section Applying overrides
For a pointer-scoped override, a linter MUST apply the severities only to results whose path is at or below the location the pointer names.
/conf/overrides/pointer-scopeA pointer-scoped override silences the rule under its pointer only.1 result
Covers /req/overrides/pointer-scope. Fixtures in conformance/overrides/pointer-scope/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthyoverrides : -files : ["document.yaml#/paths/~1legacy" ]rules : acme-operation-summary : "off"
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : {}/legacy : get : responses : {}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "path" : [ "paths" , "/pets" , "get" ] } ]
/conf/overrides/pointer-scope-atA pointer that names the result’s own location silences it.1 result
Covers /req/overrides/pointer-scope. Fixtures in conformance/overrides/pointer-scope-at/.
rules : r : given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthyoverrides : -files : ["document.yaml#/paths/~1a/get" ]rules : r : "off"
paths : /a : get : {}/b : get : {}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "paths" , "/b" , "get" ] } ]
/req/severity/numeric-equivalencelinterin section Rule state: enabled and severity
A linter MUST treat each numeric severity as equivalent to the name it is paired with in the table in #rule-severity.
/conf/severity/numeric-equivalenceA rule with severity : 0 reports with the severity of error.2 results
Covers /req/severity/numeric-equivalence. Fixtures in conformance/severity/numeric-equivalence/.
rules : acme-operation-summary : description : Every operation needs a summary.severity : 0given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "acme-operation-summary" , "severity" : 0 }, { "code" : "acme-operation-summary" , "severity" : 0 } ]
/req/severity/defaultlinterin section Rule state: enabled and severity
When neither a rule nor any definition it redefines has a severity, a linter MUST use warn.
/conf/extends/redefine-keeps-propertiesA redefinition that sets only given and then keeps the inherited rule’s severity and message.1 result
Covers /req/rules/redefinition-merge, /req/severity/default. Fixtures in conformance/extends/redefine-keeps-properties/.
extends : ./base.yamlrules : r : given : $.infothen : field : titlefunction : truthy
openapi : 3.1.0info : version : "1"
rules : r : severity : errormessage : base messagegiven : $.nothingthen : function : truthy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "severity" : 0 , "message" : "base message" } ]
/conf/severity/defaultA rule without severity reports with the severity of warn.2 results
Covers /req/severity/default. Fixtures in conformance/severity/default/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "acme-operation-summary" , "severity" : 1 }, { "code" : "acme-operation-summary" , "severity" : 1 } ]
/req/rules/recommendedlinterin section Rule state: enabled and severity
A linter MUST enable every rule a ruleset defines in its own rules whose recommended is true or absent, and MUST NOT enable one whose recommended is false unless a shorthand in an extending ruleset enables it.
/conf/rules/recommended-defaultA rule the ruleset defines runs whether recommended is true or absent.2 results
Covers /req/rules/recommended. Fixtures in conformance/rules/recommended-default/.
rules : r : given : $then : function : falsyctl : given : $then : function : falsyrecommended : true
a : 1
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "r" }, { "code" : "ctl" } ]
/conf/rules/recommended-falseA rule the ruleset defines with recommended : false does not run, while its other rules do.1 result
Covers /req/rules/recommended, /req/severity/off. Fixtures in conformance/rules/recommended-false/.
rules : r : given : $then : function : falsyrecommended : falsectl : given : $then : function : falsy
a : 1
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "ctl" } ]
/req/extends/mode-recommendedlinterin section Rule state: enabled and severity
When a ruleset is extended with the mode recommended, or without a mode, a linter MUST enable exactly those rules inherited from it whose recommended is true or absent.
/conf/extends/mode-defaultWithout a mode, only the rule that is recommended runs.1 result
Covers /req/extends/mode-recommended. Fixtures in conformance/extends/mode-default/.
extends : ./a.yaml
{}
rules : r1 : description : Not on by default.recommended : falsegiven : $then : function : falsyr2 : description : On by default.given : $then : function : falsy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r2" } ]
/conf/extends/mode-recommendedWith mode recommended, only the rule that is recommended runs.1 result
Covers /req/extends/mode-recommended. Fixtures in conformance/extends/mode-recommended/.
extends : - [./a.yaml ,recommended ]
{}
rules : r1 : description : Not on by default.recommended : falsegiven : $then : function : falsyr2 : description : On by default.given : $then : function : falsy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r2" } ]
/req/extends/mode-alllinterin section Rule state: enabled and severity
When a ruleset is extended with the mode all, a linter MUST enable every rule inherited from it.
/conf/extends/mode-allWith mode all, both rules run.2 results
Covers /req/extends/mode-all. Fixtures in conformance/extends/mode-all/.
extends : - [./a.yaml ,all ]
{}
rules : r1 : description : Not on by default.recommended : falsegiven : $then : function : falsyr2 : description : On by default.given : $then : function : falsy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "r1" }, { "code" : "r2" } ]
/conf/extends/mode-all-extra-elementWith [./base.yaml , all , extra ], the mode all applies and the extra element is ignored.1 result
Covers /req/extends/mode-all. Fixtures in conformance/extends/mode-all-extra-element/.
extends : - [./base.yaml ,all ,extra ]
openapi : 3.1.0info : version : "1" paths : {}
rules : base-rule : recommended : falsegiven : $.infothen : field : titlefunction : truthy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "base-rule" } ]
/req/extends/mode-offlinterin section Rule state: enabled and severity
When a ruleset is extended with the mode off, a linter MUST make every rule inherited from it available and MUST NOT enable any of them.
/conf/extends/mode-offWith mode off, no inherited rule runs until it is enabled by name.1 result
Covers /req/extends/mode-off. Fixtures in conformance/extends/mode-off/.
extends : - [./a.yaml ,"off" ]rules : r1 : true
{}
rules : r1 : description : Not on by default.recommended : falsegiven : $then : function : falsyr2 : description : On by default.given : $then : function : falsy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r1" } ]
/req/extends/orderlinterin section Rule state: enabled and severity
A linter MUST apply definitions and adjustments of a rule in this order: the rulesets identified by extends, in array order, then the extending ruleset’s own rules.
/conf/extends/precedenceThe extending ruleset’s definition of r wins over both extended rulesets.1 result
Covers /req/extends/order. Fixtures in conformance/extends/precedence/.
extends : - ./a.yaml - ./b.yamlrules : r : description : from Cmessage : "{{description}}" given : $then : function : falsy
{}
rules : r : description : from Amessage : "{{description}}" given : $then : function : falsy
rules : r : description : from Bmessage : "{{description}}" given : $then : function : falsy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "message" : "from C" } ]
/conf/extends/precedence-orderOf two extended rulesets defining the same rule, the later one wins.1 result
Covers /req/extends/order. Fixtures in conformance/extends/precedence-order/.
extends : - ./a.yaml - ./b.yaml
{}
rules : r : description : from Amessage : "{{description}}" given : $then : function : falsy
rules : r : description : from Bmessage : "{{description}}" given : $then : function : falsy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "message" : "from B" } ]
/req/rules/redefinition-mergelinterin section Rule state: enabled and severity
When a Rule Object redefines a rule defined earlier in that order, a linter MUST replace each property the Rule Object has and MUST keep every property it does not have.
/conf/extends/redefine-keeps-propertiesA redefinition that sets only given and then keeps the inherited rule’s severity and message.1 resultalso covers /req/severity/default; shown there
/req/shorthands/severitylinterin section Rule state: enabled and severity
For a shorthand that is a severity name, a linter MUST set the rule’s effective severity to it and MUST enable the rule, or not enable it when the name is off.
/conf/shorthands/severityA severity name enables the rule and sets its severity.1 result
Covers /req/shorthands/severity. Fixtures in conformance/shorthands/severity/.
extends : ./a.yamlrules : r : error
{}
rules : r : recommended : falseseverity : warngiven : $then : function : falsy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "severity" : 0 } ]
/req/shorthands/falselinterin section Rule state: enabled and severity
For a shorthand that is false, a linter MUST NOT enable the rule and MUST leave its effective severity unchanged.
/conf/shorthands/false-controlWithout the false shorthand, the inherited rule runs.1 result
Covers /req/shorthands/false, /req/severity/off. Fixtures in conformance/shorthands/false-control/.
extends : ./a.yaml
{}
rules : r : severity : errorgiven : $then : function : falsy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" } ]
/conf/shorthands/falsefalse disables an enabled rule.0 results
Covers /req/shorthands/false, /req/severity/off. Fixtures in conformance/shorthands/false/.
extends : ./a.yamlrules : r : false
{}
rules : r : recommended : trueseverity : errorgiven : $then : function : falsy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces no results.
/req/shorthands/truelinterin section Rule state: enabled and severity
For a shorthand that is true, a linter MUST enable the rule unless its effective severity is off, and MUST leave the effective severity unchanged, including a severity set by an earlier ruleset in the chain.
/conf/linter/walkthroughThe running example of the Linting chapter: a ruleset that extends a base, enables a rule by shorthand, declares an alias, and overrides a severity for legacy documents, linting one legacy document.3 resultsalso covers /req/extends/load, /req/overrides/scope, /req/formats/ruleset-default, /req/given/alias-expansion, /req/result/severity; shown there
/conf/shorthands/true-enablestrue enables a rule that is off by default, at the severity it was defined with.1 result
Covers /req/shorthands/true. Fixtures in conformance/shorthands/true-enables/.
extends : ./a.yamlrules : r : true
{}
rules : r : recommended : falseseverity : errorgiven : $then : function : falsy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "severity" : 0 } ]
/conf/shorthands/true-keeps-severityA rule disabled with false in one ruleset and re-enabled with true in the next keeps the severity it was defined with.1 result
Covers /req/shorthands/true. Fixtures in conformance/shorthands/true-keeps-severity/.
extends : ./b.yamlrules : r : true
{}
rules : r : recommended : trueseverity : errorgiven : $then : function : falsy
extends : ./a.yamlrules : r : false
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "severity" : 0 } ]
/conf/shorthands/true-offtrue on a rule whose severity is off does not enable it.0 results
Covers /req/shorthands/true. Fixtures in conformance/shorthands/true-off/.
extends : ./a.yamlrules : r : true
{}
rules : r : severity : "off" given : $then : function : falsy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces no results.
/req/severity/offlinterin section Rule state: enabled and severity
A linter MUST NOT evaluate a rule that is not enabled, whether its effective severity is off, a shorthand is false, or its recommended is false and nothing enables it, and MUST NOT produce results for it.
/conf/rules/recommended-falseA rule the ruleset defines with does not run, while its other rules do.1 resultalso covers /req/rules/recommended; shown there
/conf/severity/off-controlThe same rule at warn produces the results that off suppresses.2 results
Covers /req/severity/off. Fixtures in conformance/severity/off-control/.
rules : acme-operation-summary : description : Every operation needs a summary.severity : warngiven : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "acme-operation-summary" }, { "code" : "acme-operation-summary" } ]
/conf/shorthands/false-controlWithout the false shorthand, the inherited rule runs.1 resultalso covers /req/shorthands/false; shown there
/conf/severity/offA rule with severity : off produces no results although it would fail.0 results
Covers /req/severity/off. Fixtures in conformance/severity/off/.
rules : acme-operation-summary : description : Every operation needs a summary.severity : offgiven : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces no results.
/conf/shorthands/falsefalse disables an enabled rule.0 resultsalso covers /req/shorthands/false; shown there
/req/formats/identifierslinterin section Selecting documents
A linter MUST detect a format for a document when the document’s root is an object and the condition the table in #formats gives for the identifier holds, and MUST NOT detect it otherwise, among the identifiers /req/formats/detection-scope lets it detect.
/conf/formats/detect-aas2-6A document with asyncapi : 2.6.0 is aas2_6 and not aas2_5.1 result
Covers /req/formats/identifiers. Fixtures in conformance/formats/detect-aas2-6/.
rules : is-aas2_6 : formats : [aas2_6 ]given : $then : function : falsyis-aas2_5 : formats : [aas2_5 ]given : $then : function : falsy
asyncapi : 2.6.0
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "is-aas2_6" } ]
/conf/formats/detect-aas2-needs-patchasyncapi : "2.6" without a patch number is not aas2, while 2.6.0 is.1 result
Covers /req/formats/identifiers. Fixtures in conformance/formats/detect-aas2-needs-patch/.
rules : is-aas2 : formats : [aas2 ]given : $then : function : falsyis-aas3 : formats : [aas3 ]given : $then : function : falsy
asyncapi : 2.6.0
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "is-aas2" } ]
/conf/formats/detect-arazzoA document with arazzo : 1.0.1 is arazzo1_0.1 result
Covers /req/formats/identifiers. Fixtures in conformance/formats/detect-arazzo/.
rules : is-arazzo1_0 : formats : [arazzo1_0 ]given : $then : function : falsyis-oas3 : formats : [oas3 ]given : $then : function : falsy
arazzo : 1.0.1
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "is-arazzo1_0" } ]
/conf/formats/detect-json-schema-2020-12A $schema of draft 2020-12 is jsonSchemaDraft2020_12 and not draft 7.1 result
Covers /req/formats/identifiers. Fixtures in conformance/formats/detect-json-schema-2020-12/.
rules : is-jsonSchemaDraft2020_12 : formats : [jsonSchemaDraft2020_12 ]given : $then : function : falsyis-jsonSchemaDraft7 : formats : [jsonSchemaDraft7 ]given : $then : function : falsy
$schema : https://json-schema.org/draft/2020-12/schematype : object
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "is-jsonSchemaDraft2020_12" } ]
/conf/formats/detect-json-schema-looseA document with type : object and no $schema is jsonSchemaLoose but not jsonSchema.1 result
Covers /req/formats/identifiers. Fixtures in conformance/formats/detect-json-schema-loose/.
rules : is-jsonSchemaLoose : formats : [jsonSchemaLoose ]given : $then : function : falsyis-jsonSchema : formats : [jsonSchema ]given : $then : function : falsy
type : object
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "is-jsonSchemaLoose" } ]
/conf/formats/detect-number-versionAn unquoted openapi : 3.0 is the number 3, whose string form is 3, so the document is oas3 but not oas3_0.1 result
Covers /req/formats/identifiers. Fixtures in conformance/formats/detect-number-version/.
rules : is-oas3 : formats : [oas3 ]given : $then : function : falsyis-oas3_0 : formats : [oas3_0 ]given : $then : function : falsy
openapi : 3.0
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "is-oas3" } ]
/conf/formats/detect-oas2A document with swagger : "2.0" is oas2 and not oas3.1 result
Covers /req/formats/identifiers. Fixtures in conformance/formats/detect-oas2/.
rules : is-oas2 : formats : [oas2 ]given : $then : function : falsyis-oas3 : formats : [oas3 ]given : $then : function : falsy
swagger : "2.0"
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "is-oas2" } ]
/conf/formats/detect-oas3-1An OpenAPI 3.1 document is detected as oas3_1 and not as oas3_0.1 result
Covers /req/formats/identifiers. Fixtures in conformance/formats/detect-oas3-1/.
rules : is-oas3-1 : formats : [oas3_1 ]given : $then : function : falsyis-oas3-0 : formats : [oas3_0 ]given : $then : function : falsy
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "is-oas3-1" } ]
/req/formats/detection-scopelinterin section Selecting documents
A linter MUST detect a document’s formats only among the identifiers named in the formats of the ruleset, of its rules, and of the rulesets it extends and their rules. An identifier named only by a target of a scoped alias is never detected.
/conf/formats/detection-declared-onlyoas3_1 is not detected when no rule names it, so for an OpenAPI 3.1 document the alias uses its oas3 target, though an oas3_1 target comes last.1 result
Covers /req/formats/detection-scope. Fixtures in conformance/formats/detection-declared-only/.
aliases : I : targets : -formats : [oas3 ]given : ["$.info" ] -formats : [oas3_1 ]given : ["$.paths" ]rules : r : formats : [oas3 ]given : "#I" then : field : titlefunction : truthy
openapi : 3.1.0info : version : "1" paths : {}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "info" ] } ]
/req/formats/rule-scopelinterin section Selecting documents
A linter MUST evaluate a rule that has formats only against documents that have at least one of those formats.
/conf/formats/rule-scopeOf two rules scoped to different formats, only the one whose format the document has runs.1 result
Covers /req/formats/rule-scope. Fixtures in conformance/formats/rule-scope/.
rules : only-oas3 : formats : [oas3 ]given : $then : function : falsyonly-oas2 : formats : [oas2 ]given : $then : function : falsy
swagger : "2.0" info : title : Petstoreversion : 1.0.0paths : {}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "only-oas2" } ]
/req/formats/ruleset-defaultlinterin section Selecting documents
A linter MUST treat a rule without formats, defined in a ruleset with formats, as having the formats of the ruleset that defines it, also when another ruleset extends it.
/conf/formats/ruleset-defaultA rule without formats takes the ruleset’s, so it does not run on a document of another format, while a rule scoped to that format does.1 result
Covers /req/formats/ruleset-default. Fixtures in conformance/formats/ruleset-default/.
formats : [oas3 ]rules : r : given : $then : function : falsys : formats : [oas2 ]given : $then : function : falsy
swagger : "2.0" info : title : Petstoreversion : 1.0.0paths : {}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "s" } ]
/conf/formats/unrecognized-unscopedA document with none of the ruleset’s formats gets the warning, the ruleset’s own rule does not run, and an unscoped rule from an extended ruleset does.2 results
Covers /req/formats/unrecognized-document, /req/formats/ruleset-default. Fixtures in conformance/formats/unrecognized-unscoped/.
extends : ./base.yamlformats : [oas3 ]rules : r : given : $then : function : falsy
asyncapi : 2.6.0info : title : Eventsversion : 1.0.0channels : {}
rules : s : given : $then : function : falsy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "unrecognized-format" , "severity" : 1 }, { "code" : "s" } ]
/conf/linter/walkthroughThe running example of the Linting chapter: a ruleset that extends a base, enables a rule by shorthand, declares an alias, and overrides a severity for legacy documents, linting one legacy document.3 resultsalso covers /req/extends/load, /req/overrides/scope, /req/shorthands/true, /req/given/alias-expansion, /req/result/severity; shown there
/req/formats/rule-overrides-rulesetlinterin section Selecting documents
When both a rule and its ruleset have formats, a linter MUST use the rule’s and ignore the ruleset’s for that rule.
/conf/formats/rule-overrides-rulesetA rule’s own formats replaces the ruleset’s.1 result
Covers /req/formats/rule-overrides-ruleset. Fixtures in conformance/formats/rule-overrides-ruleset/.
formats : [oas3 ]rules : r : formats : [oas2 ]given : $then : function : falsy
swagger : "2.0" info : title : Petstoreversion : 1.0.0paths : {}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" } ]
/req/formats/unscoped-runs-everywherelinterin section Selecting documents
A linter MUST evaluate a rule that has no formats, in a ruleset that has none, against every document, whatever its formats.
/conf/formats/unscopedWithout formats anywhere, a rule runs on a document of no known format.1 result
Covers /req/formats/unscoped-runs-everywhere. Fixtures in conformance/formats/unscoped/.
rules : r : given : $then : function : falsy
hello : world
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" } ]
/req/formats/unrecognized-documentlinterin section Selecting documents
When a ruleset has formats, directly or through its rules, and a document has none of them, a linter MUST produce one result with the code unrecognized-format and the severity warn, and MUST still run every rule that has no formats of its own and inherits none from the ruleset.
/conf/formats/unrecognized-documentA document that has none of the ruleset’s formats gets one warning and no rule results.1 result
Covers /req/formats/unrecognized-document. Fixtures in conformance/formats/unrecognized-document/.
formats : [oas3 ]rules : r : given : $then : function : falsy
swagger : "2.0" info : title : Petstoreversion : 1.0.0paths : {}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "unrecognized-format" , "severity" : 1 } ]
/conf/formats/unrecognized-unscopedA document with none of the ruleset’s formats gets the warning, the ruleset’s own rule does not run, and an unscoped rule from an extended ruleset does.2 resultsalso covers /req/formats/ruleset-default; shown there
/req/given/evaluatelinterin section Selecting nodes
For every rule that is enabled for a document, a linter MUST evaluate each path expression in given against the document and apply the rule’s then to every matched node.
/conf/given/evaluate-all-matchesTwo operations without summary produce one result each.2 results
Covers /req/given/evaluate. Fixtures in conformance/given/evaluate-all-matches/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "acme-operation-summary" , "path" : [ "paths" , "/pets" , "get" ] }, { "code" : "acme-operation-summary" , "path" : [ "paths" , "/pets" , "post" ] } ]
/conf/given/evaluate-multiple-expressionsTwo path expressions in given each produce their results.2 results
Covers /req/given/evaluate. Fixtures in conformance/given/evaluate-multiple-expressions/.
rules : info-filled : given : - $.info.title - $.info.descriptionthen : function : truthy
info : title : "" description : ""
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "info-filled" , "path" : [ "info" , "title" ] }, { "code" : "info-filled" , "path" : [ "info" , "description" ] } ]
/req/given/no-matchlinterin section Selecting nodes
A linter MUST NOT produce a result for a rule whose given matches no node in the document.
/conf/given/matchThe same rule on a document where given matches produces results.1 result
Covers /req/given/no-match. Fixtures in conformance/given/match/.
rules : no-paths : given : $.paths[*]then : function : falsy
paths : /pets : get : {}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "no-paths" } ]
/conf/given/no-matchA rule whose given matches nothing produces no results, even though its function would fail on any node.0 results
Covers /req/given/no-match. Fixtures in conformance/given/no-match/.
rules : no-paths : given : $.paths[*]then : function : falsy
info : title : No paths here
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces no results.
/req/given/resolvedlinterin section Selecting nodes
A linter MUST evaluate given against the resolved document, unless the rule sets resolved to false, in which case it MUST evaluate given against the document as written.
/conf/given/resolved-falseWith resolved : false, given sees the $ref object, which has no type.1 result
Covers /req/given/resolved. Fixtures in conformance/given/resolved-false/.
rules : id-has-type : resolved : falsegiven : $.components.schemas.Pet.properties.idthen : field : typefunction : defined
components : schemas : Id : type : integerPet : properties : id : $ref : "#/components/schemas/Id"
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "id-has-type" } ]
/conf/given/resolved-defaultBy default given sees the resolved document, where the $ref has been replaced by a schema with type.0 results
Covers /req/given/resolved. Fixtures in conformance/given/resolved-default/.
rules : id-has-type : given : $.components.schemas.Pet.properties.idthen : field : typefunction : defined
components : schemas : Id : type : integerPet : properties : id : $ref : "#/components/schemas/Id"
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces no results.
/req/given/jsonpathlinterin section Selecting nodes
A linter MUST evaluate a JSONPath expression as JSONPath-Plus 10.4.0 JSONPATH-PLUS evaluates it, including its filter expressions in parentheses, such as $..parameters[?(@.in === "header")].
/conf/given/jsonpath-filterA filter expression in parentheses, ?(@.in === "header"), selects the header parameter with an empty name; the index in its path is a string.1 result
Covers /req/given/jsonpath. Fixtures in conformance/given/jsonpath-filter/.
rules : param-name : given : '$.parameters[?(@.in === "header")].name' then : function : truthy
parameters : -in : queryname : "" -in : headername : ""
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "param-name" , "path" : [ "parameters" , "1" , "name" ] } ]
/req/given/once-per-nodelinterin section Selecting nodes
A linter MUST apply a rule’s then to a node once, even when several of the rule’s path expressions match it.
/conf/given/once-per-nodeA node matched by three path expressions of one rule is checked once.1 result
Covers /req/given/once-per-node. Fixtures in conformance/given/once-per-node/.
rules : r : given : - $.info - $..info -"$['info']" then : field : titlefunction : truthy
openapi : 3.1.0info : version : "1" paths : {}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "info" ] } ]
/req/given/alias-expansionlinterin section Selecting nodes
A linter MUST evaluate an alias reference as the set of the named alias’s path expressions, each with the remainder of the reference after the alias name appended to it.
/conf/given/alias-expansion#Info.description is evaluated as $..info.description.1 result
Covers /req/given/alias-expansion. Fixtures in conformance/given/alias-expansion/.
aliases : Info : - $..inforules : info-description : given : "#Info.description" then : function : truthy
info : title : Petstoredescription : ""
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "info-description" , "path" : [ "info" , "description" ] } ]
/conf/linter/walkthroughThe running example of the Linting chapter: a ruleset that extends a base, enables a rule by shorthand, declares an alias, and overrides a severity for legacy documents, linting one legacy document.3 resultsalso covers /req/extends/load, /req/overrides/scope, /req/shorthands/true, /req/formats/ruleset-default, /req/result/severity; shown there
/req/aliases/composelinterin section Selecting nodes
When an alias’s path expressions contain alias references, a linter MUST expand those references in turn, until no reference remains.
/conf/aliases/composeAn alias defined through another alias expands to the full path.1 result
Covers /req/aliases/compose. Fixtures in conformance/aliases/compose/.
aliases : Info : - $..infoInfoDescription : -"#Info.description" rules : info-description : given : "#InfoDescription" then : function : truthy
info : title : Petstoredescription : ""
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "info-description" , "path" : [ "info" , "description" ] } ]
/req/aliases/scopedlinterin section Selecting nodes
For a scoped alias, a linter MUST use the given of the last target whose formats include a format of the document, and MUST match no node with it when no target does.
/conf/aliases/scoped-last-targetFor an OpenAPI 3.1 document, which has both oas3 and oas3_1, only the last matching target is used.1 result
Covers /req/aliases/scoped. Fixtures in conformance/aliases/scoped-last-target/.
aliases : I : targets : -formats : [oas3_1 ]given : ["$.paths" ] -formats : [oas3 ]given : ["$.info" ]rules : r : formats : [oas3 ,oas3_1 ]given : "#I" then : field : titlefunction : truthy
openapi : 3.1.0info : version : "1" paths : {}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "info" ] } ]
/conf/aliases/scoped-no-targetA scoped alias with no target for the document’s formats matches nothing; only the format warning is reported.1 result
Covers /req/aliases/scoped. Fixtures in conformance/aliases/scoped-no-target/.
aliases : I : targets : -formats : [oas2 ]given : ["$.info" ]rules : swagger-version : formats : [oas2 ]given : $.swaggerthen : function : truthyr : given : "#I" then : field : titlefunction : truthy
openapi : 3.1.0info : version : "1" paths : {}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "unrecognized-format" } ]
/conf/aliases/scoped-oas2On an OpenAPI 2 document, the scoped alias uses the oas2 target.1 result
Covers /req/aliases/scoped. Fixtures in conformance/aliases/scoped-oas2/.
aliases : ParameterObject : targets : -formats : [oas2 ]given : - $.parameters[*] -formats : [oas3 ]given : - $.components.parameters[*]rules : parameter-name : formats : [oas2 ,oas3 ]given : "#ParameterObject" then : field : namefunction : truthy
swagger : "2.0" info : title : Petstoreversion : 1.0.0paths : {}parameters : limit : name : "" in : query
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "parameter-name" , "path" : [ "parameters" , "limit" , "name" ] } ]
/conf/aliases/scoped-oas3On an OpenAPI 3 document, the scoped alias uses the oas3 target.1 result
Covers /req/aliases/scoped. Fixtures in conformance/aliases/scoped-oas3/.
aliases : ParameterObject : targets : -formats : [oas2 ]given : - $.parameters[*] -formats : [oas3 ]given : - $.components.parameters[*]rules : parameter-name : formats : [oas2 ,oas3 ]given : "#ParameterObject" then : field : namefunction : truthy
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : {}components : parameters : limit : name : "" in : query
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "parameter-name" , "path" : [ "components" , "parameters" , "limit" , "name" ] } ]
/req/then/apply-eachlinterin section Applying functions
For each matched node, a linter MUST apply every function invocation in then, and MUST report each problem a function returns as a result.
/conf/then/apply-eachTwo invocations that both fail on the same node produce two results.2 results
Covers /req/then/apply-each. Fixtures in conformance/then/apply-each/.
rules : title-checks : given : $.info.titlethen : -function : truthy -function : lengthfunctionOptions : min : 1
info : title : ""
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "title-checks" }, { "code" : "title-checks" } ]
/conf/then/apply-each-problemOne invocation whose function returns two problems produces two results.2 results
Covers /req/then/apply-each. Fixtures in conformance/then/apply-each-problem/.
rules : r : given : $.othen : function : schemafunctionOptions : allErrors : trueschema : type : objectrequired : [a ,b ]
o : {}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "r" }, { "code" : "r" } ]
/req/then/fieldlinterin section Applying functions
When an invocation has field, a linter MUST apply the function to each value field selects in the matched node: the property it names, the value at the path of property names and indices it names, or every value its JSONPath expression selects when it starts with $. When field selects nothing, the linter MUST apply the function once, to no value, and a result MUST have the path and range of the matched node. A result for a selected value MUST have the path of that value.
/conf/then/fieldWith field, the function sees the named property of the matched node.1 result
Covers /req/then/field. Fixtures in conformance/then/field/.
rules : info-title : given : $.infothen : field : titlefunction : truthy
info : title : "" version : 1.0.0
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "info-title" , "path" : [ "info" , "title" ] } ]
/conf/then/field-jsonpathA field that is a JSONPath expression applies the function to every value it selects.2 results
Covers /req/then/field. Fixtures in conformance/then/field-jsonpath/.
rules : r : given : $then : field : "$.items[*].name" function : truthy
items : -name : "" -name : ok -name : ""
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "r" }, { "code" : "r" } ]
/conf/then/field-missingWith field naming a property the node lacks, the function sees no value, so defined fails.1 result
Covers /req/then/field. Fixtures in conformance/then/field-missing/.
rules : info-description : given : $.infothen : field : descriptionfunction : defined
info : title : Petstore
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "info-description" , "path" : [ "info" ] } ]
/conf/then/field-pathA field that is a path of property names reaches a nested value.1 result
Covers /req/then/field. Fixtures in conformance/then/field-path/.
rules : r : given : $then : field : info.contact.emailfunction : truthy
info : contact : : ""
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "info" , "contact" , "email" ] } ]
/req/then/field-keylinterin section Applying functions
When field is @key, a linter MUST apply the function once to each property key of the matched node, and a result for a key MUST have the path of that property.
/conf/then/field-keyWith field : "@key" on a matched object, the function sees each of its property keys; the key that does not match the pattern is reported.1 result
Covers /req/then/field-key. Fixtures in conformance/then/field-key/.
rules : path-kebab : given : $.pathsthen : field : "@key" function : patternfunctionOptions : match : "^/[a-z-]+$"
paths : /pets : {}/Pets_old : {}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "path-kebab" , "path" : [ "paths" , "/Pets_old" ] } ]
/req/then/function-optionslinterin section Applying functions
A linter MUST pass the invocation’s functionOptions to the function.
/conf/then/function-optionsThe max option reaches the length function.1 result
Covers /req/then/function-options. Fixtures in conformance/then/function-options/.
rules : short-title : given : $.info.titlethen : function : lengthfunctionOptions : max : 3
info : title : Petstore
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "short-title" , "path" : [ "info" , "title" ] } ]
/req/result/fieldslinterin section Results
Every result a linter produces MUST have code, path, severity, message, and range.
/conf/result/fieldsEvery result has the five required fields.2 results
Covers /req/result/fields. Fixtures in conformance/result/fields/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "has" : [ "code" , "path" , "severity" , "message" , "range" ] }, { "has" : [ "code" , "path" , "severity" , "message" , "range" ] } ]
/req/result/codelinterin section Results
The code of a result a rule produces MUST be its rule name. The code of a result the linter produces itself MUST be one of those in the table below.
/conf/result/codeThe code of a result is the rule name.2 results
Covers /req/result/code. Fixtures in conformance/result/code/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "acme-operation-summary" }, { "code" : "acme-operation-summary" } ]
/req/result/severitylinterin section Results
The severity of a result MUST be the numeric form of the rule’s effective severity.
/conf/linter/walkthroughThe running example of the Linting chapter: a ruleset that extends a base, enables a rule by shorthand, declares an alias, and overrides a severity for legacy documents, linting one legacy document.3 resultsalso covers /req/extends/load, /req/overrides/scope, /req/shorthands/true, /req/formats/ruleset-default, /req/given/alias-expansion; shown there
/conf/result/severityA rule with severity : hint reports severity : 3.2 results
Covers /req/result/severity. Fixtures in conformance/result/severity/.
rules : acme-operation-summary : description : Every operation needs a summary.severity : hintgiven : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "severity" : 3 }, { "severity" : 3 } ]
/conf/severity/resultA rule inherited at warn and raised to error reports with the severity of error.2 results
Covers /req/result/severity. Fixtures in conformance/severity/result/.
extends : ./base.yamlrules : acme-operation-summary : error
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
rules : acme-operation-summary : description : Every operation needs a summary.severity : warngiven : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "acme-operation-summary" , "severity" : 0 }, { "code" : "acme-operation-summary" , "severity" : 0 } ]
/req/result/rangelinterin section Results
The range of a result MUST locate the value the result is about in the source text of the document: start at its first character and end just after its last, each with a zero-based line and a zero-based character counted in UTF-16 code units.
/conf/result/rangeA result about the root value starts at line 0, character 0.1 result
Covers /req/result/range. Fixtures in conformance/result/range/.
rules : root-falsy : given : $then : function : falsy
info : title : Petstore
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "root-falsy" , "range" : { "start" : { "line" : 0 , "character" : 0 } } } ]
/conf/result/range-utf16A character before the value that needs two UTF-16 code units counts as two, and the end is just after the value.1 result
Covers /req/result/range. Fixtures in conformance/result/range-utf16/.
rules : b : given : $.bthen : function : truthy
{ "a" : "😀" , "b" : "" }
Procedure
Lint document.json with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "b" , "range" : { "start" : { "line" : 0 , "character" : 17 }, "end" : { "line" : 0 , "character" : 19 } } } ]
/req/result/sourcelinterin section Results
When the document was read from a file path or URL, every result for it MUST carry that location in source, as an absolute path or the URL.
/conf/result/sourceA result for a document read from a file carries that file’s path in source.2 results
Covers /req/result/source. Fixtures in conformance/result/source/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "acme-operation-summary" , "source" : "${document}" }, { "code" : "acme-operation-summary" , "source" : "${document}" } ]
/req/result/orderlinterin section Results
A linter MUST list results in the order of the documents they belong to, as the documents were given, and within a document by the line and then the character of range.start, and then by code.
/conf/result/orderResults come in order of position, and by code at the same position, whatever the order of the rules.5 results
Covers /req/result/order. Fixtures in conformance/result/order/.
rules : zzz : given : [$.b ,$.d ]then : function : falsyaaa : given : [$.a ,$.c ]then : function : falsymmm : given : $.athen : function : falsy
a : 1b : 1c : 1d : 1
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 5 results, matching on the fields given, in any order:
[ { "code" : "aaa" , "path" : [ "a" ] }, { "code" : "mmm" , "path" : [ "a" ] }, { "code" : "zzz" , "path" : [ "b" ] }, { "code" : "aaa" , "path" : [ "c" ] }, { "code" : "zzz" , "path" : [ "d" ] } ]
/req/result/reference-targetlinterin section Results
When a rule ran against the resolved document and the value a result is about was reached through a reference, the result MUST carry the path, range, and source of the value where it is written, and a value reached through several references MUST produce one result.
/req/message/overridelinterin section The message
When a rule has message, the message of every result it produces MUST be the rule’s message with each placeholder substituted.
/conf/message/overrideA rule’s message replaces the reported message.2 results
Covers /req/message/override. Fixtures in conformance/message/override/.
rules : acme-operation-summary : description : Every operation needs a summary.message : Summary missing heregiven : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "message" : "Summary missing here" }, { "message" : "Summary missing here" } ]
/req/message/placeholderslinterin section The message
A linter MUST substitute {{ with the message the function produced, {{ with the rule’s description, {{ with the path to the offending value as a JSON Pointer RFC6901 with a leading , {{ with the last key or index of that path, and {{ with the offending value.
/conf/message/placeholder-description{{description }} resolves to the rule’s description.2 results
Covers /req/message/placeholders. Fixtures in conformance/message/placeholder-description/.
rules : acme-operation-summary : description : Every operation needs a summary.message : "{{description}}" given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "message" : "Every operation needs a summary." }, { "message" : "Every operation needs a summary." } ]
/conf/message/placeholder-path{{path }} is the JSON Pointer to the offending value, with a leading # .1 result
Covers /req/message/placeholders. Fixtures in conformance/message/placeholder-path/.
rules : r : message : "{{path}}" given : $.info.titlethen : function : falsy
info : title : x
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "message" : "#/info/title" } ]
/conf/message/placeholder-property{{property }} resolves to the key of the offending value.1 result
Covers /req/message/placeholders. Fixtures in conformance/message/placeholder-property/.
rules : info-title : message : "{{property}}" given : $.infothen : field : titlefunction : truthy
info : title : ""
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "message" : "title" } ]
/req/message/defaultlinterin section The message
When a rule has no message, the message of every result it produces MUST be the rule’s description, or the message the function produced when the rule has no description.
/conf/message/default-descriptionWithout message, the result carries the rule’s description.2 results
Covers /req/message/default. Fixtures in conformance/message/default-description/.
rules : acme-operation-summary : description : Every operation needs a summary.given : $.paths[*]['get','put','post','delete','options','head','patch','trace']then : field : summaryfunction : truthy
openapi : 3.1.0info : title : Petstoreversion : 1.0.0paths : /pets : get : responses : "200" :description : OKpost : responses : "201" :description : Created
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "message" : "Every operation needs a summary." }, { "message" : "Every operation needs a summary." } ]
/req/functions/input-typeslinterin section Core functions
A core function MUST pass when the value it is applied to is absent or of a type the function does not apply to, unless its requirement below says otherwise.
/conf/functions/input-typespattern passes on a number and on an absent value, and fails only on the string that does not match; xor passes on a string.1 result
Covers /req/functions/input-types. Fixtures in conformance/functions/input-types/.
rules : starts-with-a : given : $.*then : function : patternfunctionOptions : match : "^a" absent : given : $then : field : missingfunction : patternfunctionOptions : match : "^a" one-of : given : $.sthen : function : xorfunctionOptions : properties : [p ,q ]
n : 5s : bcdt : abc
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "starts-with-a" , "path" : [ "s" ] } ]
/req/functions/truthylinterin section truthy
truthy MUST fail when the value is absent, false, , 0, or null, and MUST pass otherwise, including for an empty array and an empty object.
/conf/functions/truthytruthy fails on false, "" , 0, and null, and passes on an empty array, an empty object, and a string.4 results
Covers /req/functions/truthy. Fixtures in conformance/functions/truthy/.
rules : all-truthy : given : $.*then : function : truthy
a : falseb : "" c : 0d : nulle : []f : {}g : x
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 4 results, matching on the fields given, in any order:
[ { "path" : [ "a" ] }, { "path" : [ "b" ] }, { "path" : [ "c" ] }, { "path" : [ "d" ] } ]
/req/functions/falsylinterin section falsy
falsy MUST fail when the value is present and not one of false, , 0, or null, and MUST pass otherwise.
/conf/functions/falsyfalsy fails on an empty array, an empty object, and a string, and passes on false, "" , 0, and null.3 results
Covers /req/functions/falsy. Fixtures in conformance/functions/falsy/.
rules : all-falsy : given : $.*then : function : falsy
a : falseb : "" c : 0d : nulle : []f : {}g : x
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 3 results, matching on the fields given, in any order:
[ { "path" : [ "e" ] }, { "path" : [ "f" ] }, { "path" : [ "g" ] } ]
/req/functions/definedlinterin section defined
defined MUST fail when the value is absent and MUST pass when it is present, whatever it is.
/conf/functions/defineddefined fails on an absent property and passes on a present one.1 result
Covers /req/functions/defined. Fixtures in conformance/functions/defined/.
rules : has-title : given : $.infothen : field : titlefunction : definedhas-description : given : $.infothen : field : descriptionfunction : defined
info : title : Petstore
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "has-description" } ]
/req/functions/undefinedlinterin section undefined
undefined MUST fail when the value is present and MUST pass when it is absent.
/conf/functions/undefinedundefined fails on a present property and passes on an absent one.1 result
Covers /req/functions/undefined. Fixtures in conformance/functions/undefined/.
rules : no-title : given : $.infothen : field : titlefunction : undefinedno-description : given : $.infothen : field : descriptionfunction : undefined
info : title : Petstore
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "no-title" } ]
/req/functions/patternlinterin section pattern
pattern MUST fail when the option match is given and the value does not match it, or when the option notMatch is given and the value matches it. Both options are regular expressions as defined by ECMAScript ECMA-262-2025.
/conf/functions/patternmatch fails on a string that does not match, notMatch fails on one that does.2 results
Covers /req/functions/pattern. Fixtures in conformance/functions/pattern/.
rules : must-match : given : $.*then : function : patternfunctionOptions : match : "^[a-z]+$" must-not-match : given : $.*then : function : patternfunctionOptions : notMatch : "^abc$"
a : abcb : ABC
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "must-match" , "path" : [ "b" ] }, { "code" : "must-not-match" , "path" : [ "a" ] } ]
/req/functions/casinglinterin section casing
casing MUST fail when the value is a non-empty string that is not written in the case named by the option type: one of flat, camel, pascal, kebab, cobol, snake, and macro, where only ASCII letters and digits count as written in a case.
/conf/functions/casingcasing with type : kebab fails on a camel-case string.1 result
Covers /req/functions/casing. Fixtures in conformance/functions/casing/.
rules : kebab : given : $.names[*]then : function : casingfunctionOptions : type : kebab
names : - kebab-case - camelCase
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "kebab" } ]
/conf/functions/casing-non-asciiOnly ASCII letters count, so éclair is not camel case; the empty string passes.1 result
Covers /req/functions/casing. Fixtures in conformance/functions/casing-non-ascii/.
rules : r : given : [$.a ,$.b ,$.c ]then : function : casingfunctionOptions : type : "camel"
a : éclairb : "" c : fooBar
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "a" ] } ]
/conf/functions/casing-typesEach case accepts a value written in it and rejects one written in another case.6 results
Covers /req/functions/casing. Fixtures in conformance/functions/casing-types/.
rules : camel : given : $.camel[*]then : function : casingfunctionOptions : type : camelpascal : given : $.pascal[*]then : function : casingfunctionOptions : type : pascalsnake : given : $.snake[*]then : function : casingfunctionOptions : type : snakemacro : given : $.macro[*]then : function : casingfunctionOptions : type : macrocobol : given : $.cobol[*]then : function : casingfunctionOptions : type : cobolflat : given : $.flat[*]then : function : casingfunctionOptions : type : flat
camel : [fooBar ,foo_bar ]pascal : [FooBar ,fooBar ]snake : [foo_bar ,foo-bar ]macro : [FOO_BAR ,foo_bar ]cobol : [FOO-BAR ,foo-bar ]flat : [foobar ,fooBar ]
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 6 results, matching on the fields given, in any order:
[ { "code" : "camel" }, { "code" : "pascal" }, { "code" : "snake" }, { "code" : "macro" }, { "code" : "cobol" }, { "code" : "flat" } ]
/req/functions/lengthlinterin section length
length MUST fail when the length of the value is below the option min or above the option max, where the length of a string is its number of UTF-16 code units, of an array its number of elements, of an object its number of properties, and of a number the number itself.
/conf/functions/lengthlength with max : 4 fails on a string of six characters and an array of five elements, and passes on a number below the bound.2 results
Covers /req/functions/length. Fixtures in conformance/functions/length/.
rules : bounded : given : $.*then : function : lengthfunctionOptions : max : 4
short : ablong : abcdefmany : [1 ,2 ,3 ,4 ,5 ]number : 3
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "path" : [ "long" ] }, { "path" : [ "many" ] } ]
/conf/functions/length-code-unitsA string’s length is its number of UTF-16 code units: é is one, an emoji two.1 result
Covers /req/functions/length. Fixtures in conformance/functions/length-code-units/.
rules : r : given : [$.e ,$.emoji ]then : function : lengthfunctionOptions : min : 2
e : "é" emoji : "😀"
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "e" ] } ]
/conf/functions/length-minlength with min : 2 fails on an object with one property and a string of one character, and passes on an object with two.2 results
Covers /req/functions/length. Fixtures in conformance/functions/length-min/.
rules : at-least-two : given : $.*then : function : lengthfunctionOptions : min : 2
o : a : 1p : a : 1b : 2s : x
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "at-least-two" , "path" : [ "o" ] }, { "code" : "at-least-two" , "path" : [ "s" ] } ]
/req/functions/alphabeticallinterin section alphabetical
alphabetical MUST fail when the elements of an array, or the keys of an object, are not in ascending order, where numbers are compared by value, strings as localeCompare of ECMAScript ECMA-402 compares them with no locale given, and two values of different types count as in order. With the option keyedBy, the elements of an array of objects are compared by that property.
/conf/functions/alphabeticalalphabetical fails on an unsorted array and passes on a sorted one.1 result
Covers /req/functions/alphabetical. Fixtures in conformance/functions/alphabetical/.
rules : sorted : given : $.*then : function : alphabetical
tags : [b ,a ]ok : [a ,b ]
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "sorted" } ]
/conf/functions/alphabetical-caseStrings are compared with localeCompare, so a comes before B and B after a.1 result
Covers /req/functions/alphabetical. Fixtures in conformance/functions/alphabetical-case/.
rules : r : given : [$.ok ,$.bad ]then : function : alphabetical
ok : [a ,B ]bad : [B ,a ]
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "bad" , "0" ] } ]
/conf/functions/alphabetical-keyed-byWith keyedBy, an array of objects is compared by that property.1 result
Covers /req/functions/alphabetical. Fixtures in conformance/functions/alphabetical-keyed-by/.
rules : tags-sorted : given : $.*then : function : alphabeticalfunctionOptions : keyedBy : name
unsorted : -name : b -name : asorted : -name : a -name : b
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "tags-sorted" } ]
/conf/functions/alphabetical-numbersNumbers are compared by value, so [2 , 10 ] is in order and [10 , 9 ] is not.1 result
Covers /req/functions/alphabetical. Fixtures in conformance/functions/alphabetical-numbers/.
rules : r : given : [$.ok ,$.bad ]then : function : alphabetical
ok : [2 ,10 ]bad : [10 ,9 ]
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "bad" , "0" ] } ]
/req/functions/enumerationlinterin section enumeration
enumeration MUST fail when the value is not one of the elements of the option values, compared by JSON type and value, so that is not 1 while 1.0 is.
/conf/functions/enumerationenumeration fails on a value outside values.1 result
Covers /req/functions/enumeration. Fixtures in conformance/functions/enumeration/.
rules : allowed : given : $.*then : function : enumerationfunctionOptions : values : [a ,b ]
x : cy : a
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "path" : [ "x" ] } ]
/conf/functions/enumeration-types"1" is not one of [1 ], while 1.0 is.1 result
Covers /req/functions/enumeration. Fixtures in conformance/functions/enumeration-types/.
rules : r : given : [$.s ,$.n ]then : function : enumerationfunctionOptions : values : [1 ]
s : "1" n : 1.0
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "s" ] } ]
/req/functions/schemalinterin section schema
schema MUST fail when the value is absent, or when it does not validate against the JSON Schema given as the option schema, asserting the format keyword and resolving a $ref within the option against the option itself, with one problem for the first failed keyword, or one per failed keyword when the option allErrors is true. The schema is read in the draft the option dialect names, as the table above defines each draft; with dialect absent or auto, in the draft its $schema names, and in draft 7 when it names none.
/conf/functions/schemaschema fails on a value that does not validate against the given JSON Schema.1 result
Covers /req/functions/schema. Fixtures in conformance/functions/schema/.
rules : strings : given : $.*then : function : schemafunctionOptions : schema : type : string
x : 1y : text
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "path" : [ "x" ] } ]
/conf/functions/schema-absentschema fails on an absent value.1 result
Covers /req/functions/schema. Fixtures in conformance/functions/schema-absent/.
rules : contact-shape : given : $.infothen : field : contactfunction : schemafunctionOptions : schema : type : object
info : title : Petstore
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "contact-shape" } ]
/conf/functions/schema-all-errorsWith allErrors : true, a value that fails two keywords gets two problems.2 results
Covers /req/functions/schema. Fixtures in conformance/functions/schema-all-errors/.
rules : r : given : $.vthen : function : schemafunctionOptions : schema : {"type" :"string" ,"minLength" :3 ,"pattern" :"^y" }allErrors : true
v : x
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "v" ] }, { "code" : "r" , "path" : [ "v" ] } ]
/conf/functions/schema-default-draftWithout $schema or dialect, the schema is read as draft 7: prefixItems is ignored, required is not.1 result
Covers /req/functions/schema. Fixtures in conformance/functions/schema-default-draft/.
rules : prefix : given : $.tthen : function : schemafunctionOptions : schema : type : arrayprefixItems : -type : stringneeds-a : given : $.othen : function : schemafunctionOptions : schema : required : [a ]
t : [1 ]o : {}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "needs-a" } ]
/conf/functions/schema-dialectWith dialect : draft2020-12, prefixItems applies.1 result
Covers /req/functions/schema. Fixtures in conformance/functions/schema-dialect/.
rules : prefix : given : $.tthen : function : schemafunctionOptions : dialect : draft2020-12schema : type : arrayprefixItems : -type : string
t : [1 ]
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "prefix" } ]
/conf/functions/schema-first-errorWithout allErrors, a value that fails two keywords gets one problem.1 result
Covers /req/functions/schema. Fixtures in conformance/functions/schema-first-error/.
rules : r : given : $.vthen : function : schemafunctionOptions : schema : {"type" :"string" ,"minLength" :3 ,"pattern" :"^y" }
v : x
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "v" ] } ]
/conf/functions/schema-formatThe format keyword is asserted, so not-an-email fails format : email.1 result
Covers /req/functions/schema. Fixtures in conformance/functions/schema-format/.
rules : r : given : [$.bad ,$.ok ]then : function : schemafunctionOptions : schema : {"type" :"string" ,"format" :"email" }
bad : not-an-emailok : a@b.co
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "bad" ] } ]
/conf/functions/schema-refA $ref inside the option resolves against the option’s own $defs.1 result
Covers /req/functions/schema. Fixtures in conformance/functions/schema-ref/.
rules : r : given : $.vthen : function : schemafunctionOptions : schema : {"$ref" :"#/$defs/x" ,"$defs" : {"x" : {"type" :"integer" }}}
v : text
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "v" ] } ]
/req/functions/xorlinterin section xor
xor MUST fail unless exactly one of the properties named by the option properties is present on the value, where a property is present when the object has the key, whatever its value.
/conf/functions/xorxor fails unless exactly one of properties is present.2 results
Covers /req/functions/xor. Fixtures in conformance/functions/xor/.
rules : one-of : given : $.*then : function : xorfunctionOptions : properties : [p ,q ]
both : p : 1q : 1one : p : 1neither : {}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "path" : [ "both" ] }, { "path" : [ "neither" ] } ]
/conf/functions/xor-nullA key whose value is null counts as present, so {a : null , b : 1 } has two of the properties.1 result
Covers /req/functions/xor. Fixtures in conformance/functions/xor-null/.
rules : r : given : [$.one ,$.two ]then : function : xorfunctionOptions : properties : ["a" ,"b" ]
one : {a : null }two : {a : null ,b : 1 }
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "two" ] } ]
/req/functions/orlinterin section or
or MUST fail unless at least one of the properties named by the option properties is present on the value, where a property is present when the object has the key, whatever its value.
/conf/functions/oror fails unless at least one of properties is present.1 result
Covers /req/functions/or. Fixtures in conformance/functions/or/.
rules : any-of : given : $.*then : function : orfunctionOptions : properties : [p ,q ]
both : p : 1q : 1one : p : 1neither : {}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "path" : [ "neither" ] } ]
/conf/functions/or-nullA key whose value is null counts as present, so {a : null } satisfies or and {} does not.1 result
Covers /req/functions/or. Fixtures in conformance/functions/or-null/.
rules : r : given : [$.one ,$.none ]then : function : orfunctionOptions : properties : ["a" ,"b" ]
one : {a : null }none : {}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "path" : [ "none" ] } ]
/req/functions/unreferenced-reusable-objectlinterin section unreferencedReusableObject
unreferencedReusableObject, applied to an object of reusable definitions, MUST fail once for each member that no $ref in the documents being linted points to, where the option reusableObjectsLocation is the JSON Pointer RFC6901 of that object in the document.
/conf/functions/unreferenced-reusable-objectunreferencedReusableObject reports a member of the container that nothing references.1 result
Covers /req/functions/unreferenced-reusable-object. Fixtures in conformance/functions/unreferenced-reusable-object/.
rules : no-unused-schemas : resolved : falsegiven : $.components.schemasthen : function : unreferencedReusableObjectfunctionOptions : reusableObjectsLocation : "#/components/schemas"
paths : /pets : get : responses : "200" :content : application/json : schema : $ref : "#/components/schemas/Used" components : schemas : Used : type : objectUnused : type : object
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "path" : [ "components" , "schemas" , "Unused" ] } ]
Conformance class /conf/linter-custom-functions
/req/custom-functions/missinglinterin section The contract
A linter MUST reject a ruleset that names a function it cannot load.
/conf/custom-functions/presentA ruleset naming a function whose file exists is loaded and the function runs.1 result
Covers /req/custom-functions/missing. Fixtures in conformance/custom-functions/present/.
functions : - isEmptyTitlerules : title-filled : given : $.info.titlethen : function : isEmptyTitle
info : title : ""
module. exports= ( v) => ( typeof v=== 'string' && v. trim() !== '' ? [] : [{ message: 'Title is empty.' }]);
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "title-filled" } ]
/conf/custom-functions/missingA ruleset naming a function whose file does not exist is rejected.rejected
Covers /req/custom-functions/missing. Fixtures in conformance/custom-functions/missing/.
functions : - doesNotExistrules : r : given : $then : function : doesNotExist
{}
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
The linter rejects the ruleset and produces no results.
/req/custom-functions/invokelinterin section The contract
A linter MUST call a custom function with the value the invocation targets and the invocation’s functionOptions, and MUST report each problem the function returns as a result whose message is the problem’s message.
/conf/custom-functions/invokeThe function receives the value and the options, and each problem it returns becomes a result with its message.2 results
Covers /req/custom-functions/invoke. Fixtures in conformance/custom-functions/invoke/.
functions : - forbiddenWordsrules : no-forbidden-words : given : $.info.titlethen : function : forbiddenWordsfunctionOptions : forbidden : [evil ,bad ]
info : title : An evil and bad title
module. exports= ( targetVal, options) => { const problems= []; for ( const wordof options. forbidden) { if ( String( targetVal). includes( word)) { problems. push({ message: Contains& quot; ${ word} & quot;. }); } } return problems; };
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "no-forbidden-words" , "message" : "Contains \"evil\"." }, { "code" : "no-forbidden-words" , "message" : "Contains \"bad\"." } ]
/req/custom-functions/problem-pathlinterin section The contract
A linter MUST report a problem that carries a path at that path in the document, a problem without one at the matched node, and a problem whose path is not in the document at the document’s root.
/conf/custom-functions/problem-pathA problem with a path is reported there; one whose path is not in the document is reported at the root.2 results
Covers /req/custom-functions/problem-path. Fixtures in conformance/custom-functions/problem-path/.
functions : [pathed ,elsewhere ]rules : pathed : given : $.infothen : function : pathedelsewhere : given : $.infothen : function : elsewhere
info : title : x
module. exports= ( value, options) => [{ message: 'at the title' , path: [ 'info' , 'title' ] }];
module. exports= ( value, options) => [{ message: 'nowhere' , path: [ 'nope' , 'deeper' ] }];
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 2 results, matching on the fields given, in any order:
[ { "code" : "pathed" , "path" : [ "info" , "title" ] }, { "code" : "elsewhere" , "path" : [] } ]
/req/custom-functions/no-problemslinterin section The contract
A linter MUST treat a custom function that returns nothing as returning no problems.
/conf/custom-functions/returns-nothingA function that returns nothing reports no problems, while one that returns a problem does.1 result
Covers /req/custom-functions/no-problems. Fixtures in conformance/custom-functions/returns-nothing/.
functions : [nothing ,one ]rules : quiet : given : $.infothen : function : nothingloud : given : $.infothen : function : one
info : title : x
module. exports= ( value, options) => { return ; };
module. exports= ( value, options) => [{ message: 'one problem' }];
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "loud" , "path" : [ "info" ] } ]
Conformance class /conf/linter-custom-functions-javascript
/req/custom-functions/javascript-loadlinterin section JavaScript binding
A linter MUST load each name in functions from /.js, resolved relative to the ruleset file, with functions as the directory when functionsDir is absent.
/conf/custom-functions/load-custom-dirWith functionsDir, the function is loaded from that directory.1 result
Covers /req/custom-functions/javascript-load. Fixtures in conformance/custom-functions/load-custom-dir/.
functions : - isEmptyTitlefunctionsDir : ./checksrules : title-filled : given : $.info.titlethen : function : isEmptyTitle
info : title : " "
module. exports= ( targetVal) => { if ( typeof targetVal!== 'string' || targetVal. trim() === '' ) { return [{ message: 'Title is empty.' }]; } return []; };
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "title-filled" , "message" : "Title is empty." } ]
/conf/custom-functions/load-default-dirA function named in functions is loaded from functions/<name>.js next to the ruleset.1 result
Covers /req/custom-functions/javascript-load. Fixtures in conformance/custom-functions/load-default-dir/.
functions : - isEmptyTitlerules : title-filled : given : $.info.titlethen : function : isEmptyTitle
info : title : " "
module. exports= ( targetVal) => { if ( typeof targetVal!== 'string' || targetVal. trim() === '' ) { return [{ message: 'Title is empty.' }]; } return []; };
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "title-filled" , "message" : "Title is empty." } ]
/req/custom-functions/javascript-promiselinterin section JavaScript binding
When a JavaScript custom function returns a promise, a linter MUST wait for it and use the problems it resolves to.
/conf/custom-functions/javascript-promiseA function that returns a promise is awaited, and the problems it resolves to are reported.1 result
Covers /req/custom-functions/javascript-promise. Fixtures in conformance/custom-functions/javascript-promise/.
functions : [later ]rules : r : given : $.infothen : function : later
info : title : x
module. exports= async ( value, options) => [{ message: 'from a promise' }];
Procedure
Lint document.yaml with ruleset.yaml and collect the results as JSON.
Expected
Linting produces exactly 1 result, matching on the fields given, in any order:
[ { "code" : "r" , "message" : "from a promise" } ]
Appendix C: Changes
There is no release yet. From the first release on, this appendix lists what changed since the latest one.
Appendix D: Using the schema
This appendix shows how to use the ruleset schema while writing rulesets. The examples use https://spec.openlint.org/v1/ruleset.schema.json, which follows the newest 1.x release; see § 1.2 Versions of this document. The format reserves no $schema key and rejects unknown root properties, so bind the schema from outside the document.
For a YAML ruleset, add a modeline comment:
# yaml-language-server: $schema=https://spec.openlint.org/v1/ruleset.schema.json extends : spectral:oas
For a JSON ruleset in VS Code, map the schema to your ruleset’s file name in .vscode/settings.json:
{ "json.schemas" : [ { "fileMatch" : [ "ruleset.json" ], "url" : "https://spec.openlint.org/v1/ruleset.schema.json" } ] }
In CI, any JSON Schema 2020-12 validator works:
npx ajv-cli validate --spec= draft2020 --strict= false\ -s ruleset.schema.json\ -d ruleset.yaml
Validating a ruleset against the schema catches misspelled keywords and invalid overrides before any document is linted. It does not catch everything. A JSON Schema validates the shape of one parsed value, so it cannot check that the file parses, that a name refers to something declared elsewhere, or that function options match the named function. The schema accepts rulesets that violate these requirements, and a linter rejects them:
- /req/ruleset/document: A ruleset MUST be a JSON RFC8259 or YAML YAML-1.2.2 document.
- /req/ruleset/single-document: A ruleset written in YAML MUST contain exactly one YAML document.
- /req/shorthands/inherited-only: A shorthand in the root
rulesMUST name a rule the ruleset inherits throughextends. - /req/aliases/no-cycle: An alias MUST NOT refer to itself, directly or through other aliases.
- /req/given/alias-defined: An alias reference in a rule MUST name an alias declared under
aliasesin the ruleset whoserulescontain the rule or, for a rule in a file-scoped override, in that override or that ruleset. When the rule redefines a rule inherited throughextends, the ruleset that defines the inherited rule MUST declare that alias too. - /req/then/function-known:
functionMUST name a #core-functions|core function or a custom function declared infunctions. - /req/then/function-options-valid: When
functionnames a core function,functionOptionsMUST satisfy that function’s options in #core-functions: only the options its table lists, with the types listed, every required option present, and nofunctionOptionsat all for a function that takes no options.
A linter also rejects, for the documents an override applies to, what /req/overrides/checked-when-applied lists. The schema checks that a given value begins with $ or , but does not parse it as JSONPath. A syntactically invalid JSONPath expression passes the schema and fails at lint time.
Appendix E: Provenance
The format described here originates in Spectral [SPECTRAL] by Stoplight, licensed Apache-2.0. OpenLint, the reference implementation, is openlint/openlint, forked from Spectral; the issues in the working draft describe its behavior by name. The specification and schema were first derived from Spectral’s validation meta-schemas and guides. The OpenLint community drafted them in api-commons before the project had its name, and then moved them to openlint/spec. Its conformance classes follow the model of the OGC API standards.