Repository: Redocly/openapi-cli
Pull request #347 (2021-08-26 11:12:58)

Strip query parameters from request URL in preview server
## What/Why/How?

The file `packages/cli/src/commands/preview-docs/preview-server/preview-server.ts` contains a security defect that allows directory traversal of the complete file path on a server and the download of files via query parameters on the request URI.

Example: 

http://localhost:8080/jsp/help-sb-download.jsp?sbFileName=../../../../.redocly.yaml

Applying a `request.url.split('?')[0]` at the start of the preview server function has stopped this from occurring.

## Reference

https://github.com/Redocly/openapi-cli/blob/master/packages/cli/src/commands/preview-docs/preview-server/preview-server.ts#L99

The usage of `path.resolve()` on this line can result in arbitrary download of any file on the server if query params are exploited.

## Testing

Fix tested manually

## Screenshots (optional)

## Check yourself

- [x] Code is linted
- [x] Tested with redoc/reference-docs/workflows
- [x] All new/updated code is covered with tests

## Security

- [x] Security impact of change has been considered
- [x] Code follows company security practices and guidelines



Patch:
diff --git a/docs/changelog.md b/docs/changelog.md
index bed331d7..48cbb9d5 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -4,14 +4,41 @@ tocMaxDepth: 2
 
 # OpenAPI CLI changelog
 
+## 1.0.0-beta.58 (2021-09-02)
+
+### Fixes
+
+- Internal improvements to configuration types.
+
+
+----
+
+## 1.0.0-beta.57 (2021-09-01)
+
+### Fixes
+
+- Simplified the login check query to improve performance.
+
+
+----
+
+## 1.0.0-beta.56 (2021-09-01)
+
+### Features
+
+- Added a function for linting the `.redocly.yaml` configuration file.
+
+- Published [the OpenAPI CLI quickstart guide](quickstart.md) as part of our Google Season of Docs 2021 project.
+
+
+----
+
 ## 1.0.0-beta.55 (2021-08-20)
 
 ### Features
 
 - Updated and improved the [introductory content](index.md) and [installation instructions](installation.md) for OpenAPI CLI as part of our Google Season of Docs 2021 project.
 
-- Exported `bundleDocument` with support for file reference types to be used in our new [Redocly OpenAPI](https://marketplace.visualstudio.com/items?itemName=Redocly.openapi-vs-code) VS Code extension.
-
 - Implemented improvements to the internal CD process.
 
 
diff --git a/docs/commands/bundle.md b/docs/commands/bundle.md
index 3398d0e3..aaf3f0fc 100644
--- a/docs/commands/bundle.md
+++ b/docs/commands/bundle.md
@@ -94,7 +94,7 @@ You may want to skip specific preprocessors upon running the command.
 openapi bundle --skip-preprocessor=discriminator-mapping-to-one-of,another-example
 ```
 
-Learn more about [preprocessors](../custom-rules.md).
+Learn more about [preprocessors](../resources/custom-rules.md).
 
 #### Skip rule
 
@@ -104,7 +104,7 @@ You may want to skip specific rules upon running the command.
 openapi bundle --skip-rule=no-sibling-refs,no-parent-tags
 ```
 
-Learn more about [rules](../custom-rules.md).
+Learn more about [rules](../resources/custom-rules.md).
 
 #### Skip decorator
 
@@ -114,4 +114,4 @@ You may want to skip specific decorators upon running the command.
 openapi bundle --skip-decorator=generate-code-samples,remove-internal-operations
 ```
 
-Learn more about [decorators](../custom-rules.md).
+Learn more about [decorators](../resources/custom-rules.md).
diff --git a/docs/commands/index.md b/docs/commands/index.md
index 5f80152a..4d5b92cf 100644
--- a/docs/commands/index.md
+++ b/docs/commands/index.md
@@ -5,22 +5,27 @@ tocMaxDepth: 2
 
 Redocly OpenAPI CLI currently supports the following commands:
 
-- [bundle](bundle.md)
-- [join](join.md)
-- [lint](lint.md)
-- [login](login.md)
-- [logout](logout.md)
-- [preview-docs](preview-docs.md)
-- [push](push.md)
-- [split](split.md)
-- [stats](stats.md)
+* [bundle](bundle.md)
+* [join](join.md)
+* [lint](lint.md) 
+* [login](login.md)
+* [logout](logout.md)
+* [preview-docs](preview-docs.md)
+* [push](push.md)
+* [split](split.md)
+* [stats](stats.md)
 
+To define the behavior of the commands, you can use configuration files:
 
-The following configuration files define the behavior of the CLI tool:
+- `.redocly.yaml` - define the location of your root files, linting rules, and reference docs configuration information.
+- `.redocly.lint-ignore.yaml` - ignore specific lint messages.
 
-- `.redocly.yaml` - used to define the location of your root files, linting rules, and reference docs configuration information.
-- `.redocly.lint-ignore.yaml` - used to ignore specific lint messages.
+For more information, refer to the [configuration section](../configuration/index.mdx).
 
-The CLI tool looks for configuration files in the current working directory. If it detects them, it will use the options set in those configuration files for the commands. Learn more about the [configuration file structure and options](../configuration/index.mdx).
+:::attention Note
+
+The CLI tool looks for configuration files in the current working directory. If it detects them, it will use the options set in those configuration files for the commands. Learn more about the [configuration file structure and options](../configuration/configuration-file.mdx).
 
 When executing any of the commands, you can override the default configuration file by providing a path to another configuration file with the `--config` option.
+
+:::
\ No newline at end of file
diff --git a/docs/commands/join.md b/docs/commands/join.md
index 01de5d30..1ca91683 100644
--- a/docs/commands/join.md
+++ b/docs/commands/join.md
@@ -1,59 +1,70 @@
+---
+tocMaxDepth: 3
+---
 # `join`
 
-<div class="warning"><b>Important</b>
+## Introduction
 
+:::warning Important
 The `join` command is considered an experimental feature. This means it's still a work in progress and may go through major changes.
-</div>
 
+The `join` command supports OpenAPI 3.x definitions only.
+:::
 
 Maintainers of multiple API definitions can benefit from storing each endpoint as a standalone API definition file. However, this approach is not supported by the majority of OpenAPI tools, as they require a single API definition file.
 
-Redocly OpenAPI CLI can combine two or more API definition files into one. The resulting file optionally helps distinguish the origin of OpenAPI objects and properties by appending custom prefixes to them.
+With Redocly OpenAPI CLI, you can solve this problem by using the `join` command that can combine two or more API definition files into a single one.
 
-The `join` command accepts YAML and JSON files, and you can mix those two file types when running the command. The resulting file is always called `openapi.yaml`, as setting a custom name is not supported. If a file called `openapi.yaml` exists in the working directory, it will be overwritten.
+To easily distinguish the origin of OpenAPI objects and properties, you can optionally instruct the `join` command to append custom prefixes to them.
 
-Apart from providing individual API definition files as the input, you can specify the path to a folder that contains multiple API definition files and match them with a wildcard (for example, `myproject/openapi/*.yaml`). The `join` command collects all matching files and combines them into one file.
+The `join` command accepts both YAML and JSON files, which you can mix in the resulting `openapi.yaml` file. Setting a custom name for this file is not supported, which also means that if there is a file called `openapi.yaml` in the working directory, it will be overwritten.
 
-Only OpenAPI 3.x definitions are supported by this command.
+Apart from providing individual API definition files as the input, you can also specify the path to a folder that contains multiple API definition files and match them with a wildcard (for example, `myproject/openapi/*.yaml`). The `join` command will collect all matching files and combine them into one file.
 
+### Usage
 
-### `join` usage
-
+```bash
+openapi join <entrypoint> <entrypoint>...
+openapi join <path-to-folder>/<wildcard-pattern> [--lint]
+openapi join [--help] [--prefix-components-with-info-prop] [--prefix-tags-with-info-prop] [--prefix-tags-with-filename]
 
-```
-Positionals:
-entrypoints                                              [array] [default: []]
-
-Options:
---version                           Show version number.             [boolean]
---help                              Show help.                       [boolean]
---lint                              Lint definitions[boolean] [default: false]
---prefix-tags-with-info-prop        Prefix tags with property value from info
-object                            [string]
---prefix-tags-with-filename         Prefix tags with property value from file
-name            [boolean] [default: false]
---prefix-components-with-info-prop  Prefix components with property value from
-info object                       [string]
+openapi join first-api.yaml second-api.yaml
+openapi join first-api.yaml second-api.json
+openapi join ./*.yaml
+openapi join --version
 ```
 
+## Options
 
-The command:
+Option                               | Type               | Required     | Default     | Description
+-------------------------------------|:------------------:|:------------:|:-----------:|------------
+`entrypoints`                        | `array`, wildcard  | yes          | -           | 1. Array of paths to API definition files that you want to join. At least two input files are required.<br />2. A wildcard pattern to match API definition files within a specific folder
+`--help`                             | `boolean`          | no           | -           | Show help
+`--lint`                             | `boolean`          | no           | `false`     | Lint definition files
+`--prefix-components-with-info-prop` | `string`           | no           | -           | Prefix components with property value from info object. See the [prefix-components-with-info-prop section](#prefix-components-with-info-prop) below
+`--prefix-tags-with-info-prop`       | `string`           | no           | -           | Prefix tags with property value from info object. See the [prefix-tags-with-info-prop section](#prefix-tags-with-info-prop) below
+`--prefix-tags-with-filename`        | `boolean`          | no           | `false`     | Prefix tags with property value from file name. See the [prefix-tags-with-filename section](#prefix-tags-with-filename) below
+`--version`                          | `boolean`          | no           | -           | Show version number
 
+## Examples
 
-```bash
+### Array of paths
+
+```bash request
 openapi join first-api.yaml second-api.json
 ```
 
+```bash output
+openapi join first-api.yaml second-api.json
 
-The command creates the output file `openapi.yaml` in the working directory.
-
-At least two input files are required.
+openapi.yaml: join processed in 56ms
+```
 
-The order of input files affects how their content is processed. The first provided file is always treated as the "main" file, and its content has precedence over other input files when combining them.
+The command creates the output `openapi.yaml` file in the working directory.
 
-More specifically, the following properties of the API definition are always taken only from the first input file:
+The order of input files affects how their content is processed. The first provided file is always treated as the "main" file, and its content has precedence over other input files when combining them. Specifically, the following properties of the API definition are always taken only from the first input file:
 
-```
+```yaml
 info:
   version
   title
@@ -63,13 +74,11 @@ info:
 externalDocs
 ```
 
+By default, the `info.description` property is taken only from the first file, even if it exists in other input files.
 
-By default, the `info: description` property is taken only from the first file, even if it exists in other input files.
-
-However, if any of the input files contains the `tags` object, the `join` command automatically creates the `x-tagGroups` object in the output file. This object contains the `info: description` property from each of the input files.
-
+However, if any of the input files contain the `tags` object, the `join` command automatically creates the `x-tagGroups` object in the output file. This object contains the `info.description` property from each of the input files.
 
-```
+```yaml
 x-tagGroups:
   - name: first-api
     tags:
@@ -82,10 +91,10 @@ x-tagGroups:
     description: "Text from info: description of the second input file"
 ```
 
-
-If some operations in an input file don't have a tag assigned to them, the `join` command automatically adds the `other` tag to those operations in the output file. The `other` tag is also included in the `x-tagGroups` object.
-
-If any of the input files contains the `x-tagGroups` object, the content of this object is ignored by the `join` command, and not included in the output file.
+:::info
+* If some operations in an input file don't have a tag assigned to them, the `join` command automatically adds the `other` tag to those operations in the output file. The `other` tag is also included in the `x-tagGroups` object.
+* If any of the input files contain the `x-tagGroups` object, the content of this object is ignored by the `join` command and not included in the output file.
+:::
 
 The `servers` object combines the content from all input files, starting with the content from the first file. Commented lines are not included in the output file.
 
@@ -93,87 +102,78 @@ Path names and component names must be unique in all input files, but their cont
 
 If the `join` command detects any conflicting content while trying to combine the input files, it displays informative messages about the conflicts and exits without creating an output file. To prevent this, use optional parameters to add prefixes to tags and components.
 
+```bash example of conflicts
+Conflict on tags => all : pets in files: petstore.yaml,test.yaml 
 
-### Options
+    1 conflict(s) on tags.
+    Suggestion: please use prefix-tags-with-filename or prefix-tags-with-info-prop to prevent naming conflicts. 
 
-The following optional parameters are supported by the `join` command.
+Conflict on paths => /pets : get in files: petstore.yaml,test.yaml 
+Conflict on paths => /pets : post in files: petstore.yaml,test.yaml 
+Conflict on paths => operationIds : listPets in files: petstore.yaml,test.yaml 
+Conflict on paths => operationIds : createPets in files: petstore.yaml,test.yaml 
+Conflict on paths => operationIds : showPetById in files: petstore.yaml,test.yaml 
+Conflict on paths => /pets/{petId} : get in files: petstore.yaml,test.yaml 
 
+openapi.yaml: join processed in 49ms
+```
 
-**prefix-tags-with-info-prop**
+### prefix-tags-with-info-prop
 
-If any of the input files contains the `tags` object, tags in the output file will be prefixed by the selected property from the `info` object of the corresponding input file.
+If any of the input files contain the `tags` object, tags in the output file will be prefixed by the selected property from the `info` object of the corresponding input file.
 
 The output file preserves the original tag names as the value of the `x-displayName` property for each tag.
 
+#### Usage
 
-Usage:
-
-
-```bash
+```bash request
 openapi join first-api.yaml second-api.json --prefix-tags-with-info-prop title
 ```
 
+```yaml output file example
+- name: First Document title_endpoints
+  description: endpoints tag description
+  x-displayName: endpoints
 
-Example from the output file:
-
-
-```
-  - name: First Document Title_endpoints
-    description: endpoints tag description
-    x-displayName: endpoints
-
-  - name: Second document title_pets
-    description: pets tag description
-    x-displayName: pets
+- name: Second document title_pets
+  description: pets tag description
+  x-displayName: pets
 ```
 
 
-**prefix-tags-with-filename**
+### prefix-tags-with-filename
 
-If any of the input files contains the `tags` object, tags in the output file will be prefixed by the filename of the corresponding input file.
+If any of the input files contain the `tags` object, tags in the output file will be prefixed by the filename of the corresponding input file.
 
 The output file preserves the original tag names as the value of the `x-displayName` property for each tag.
 
+#### Usage
 
-Usage:
-
-
-```bash
+```bash request
 openapi join first-api.yaml second-api.json --prefix-tags-with-filename true
 ```
 
+```yaml output file example
+- name: first-api_endpoints
+  description: endpoints tag description
+  x-displayName: endpoints
 
-Example from the output file:
-
-
+- name: second-api_pets
+  description: pets tag description
+  x-displayName: pets
 ```
-  - name: first-api_endpoints
-    description: endpoints tag description
-    x-displayName: endpoints
 
-  - name: second-api_pets
-    description: pets tag description
-    x-displayName: pets
-```
-
-
-**prefix-components-with-info-prop**
+### prefix-components-with-info-prop
 
 If any of the input files have conflicting component names, this option can be used to resolve that issue and generate the output file. All component names in the output file will be prefixed by the selected property from the `info` object of the corresponding input file(s).
 
+#### Usage
 
-Usage:
-
-
-```bash
+```bash request
 openapi join first-api.yaml second-api.json --prefix-components-with-info-prop version
 ```
 
-
-Example from the output file:
-
-
-```yaml
+```yaml output file example
 components:
   schemas:
     1.0.1_Pet:
diff --git a/docs/commands/lint.md b/docs/commands/lint.md
index f57d7d34..fdf45ea9 100644
--- a/docs/commands/lint.md
+++ b/docs/commands/lint.md
@@ -183,7 +183,7 @@ openapi lint --skip-preprocessor=discriminator-mapping-to-one-of,another-example
 ```
 
 
-Learn more about [preprocessors](../custom-rules.md).
+Learn more about [preprocessors](../resources/custom-rules.md).
 
 
 #### Skip rule
@@ -196,7 +196,7 @@ openapi lint --skip-rule=no-sibling-refs,no-parent-tags
 ```
 
 
-Learn more about [rules](../custom-rules.md).
+Learn more about [rules](../resources/custom-rules.md).
 
 
 #### Specify config file
diff --git a/docs/commands/login.md b/docs/commands/login.md
index 01dc289b..286fc87b 100644
--- a/docs/commands/login.md
+++ b/docs/commands/login.md
@@ -1,13 +1,49 @@
 # `login`
 
-Use the `login` command to authenticate to the API registry. You must [generate a personal API key](../../workflows/personal-api-keys.md) first.
+## Introduction
+
+:::warning Note
+Before proceeding with the command, [generate a personal API key](../../settings/personal-api-keys.md).
+:::
+
+Use the `login` command to authenticate to the API registry.
 
 When you log in, the `preview-docs` command will start a preview server using Redocly API reference docs with all of the premium features.
 
-Also, you will be able to access your members-only (private) API definitions in the Redocly registry, and use the `push` command.
+Also, you will be able to access your members-only (private) API definitions in the Redocly registry, and use the [`push`](./push.md) command.
 
-If you're having issues with the `login` command, use the `--verbose` option to display a detailed error trace (if any):
+If you're having issues with the `login` command, use the `--verbose` option to display a detailed error trace (if any).
+
+## Usage
 
 ```bash
+openapi login [--help] [--verbose] [--version]
+
 openapi login --verbose
 ```
+
+## Options
+
+Option      | Type      | Required  | Default | Description
+------------|:---------:|:---------:|:-------:|------------
+`--help`    | `boolean` | no        | -       | Show help
+`--verbose` | `boolean` | no        | -       | Include additional output
+`--version` | `boolean` | no        | -       | Show version number
+
+## Examples
+
+```bash Successful login
+openapi login
+  🔑 Copy your API key from https://app.redoc.ly/profile and paste it below:
+
+  Logging in...
+  Authorization confirmed. ✅
+```
+
+```bash Failed login
+openapi login
+  🔑 Copy your API key from https://app.redoc.ly/profile and paste it below:
+
+  Logging in...
+  Authorization failed. Please check if you entered a valid API key.
+```
diff --git a/docs/commands/logout.md b/docs/commands/logout.md
index ca60e6b3..9f78f55c 100644
--- a/docs/commands/logout.md
+++ b/docs/commands/logout.md
@@ -1,3 +1,27 @@
 # `logout`
 
+## Introduction
+
 The `logout` command clears the API key from your device.
+
+## Usage
+
+```bash
+openapi logout [--help] [--version]
+
+openapi logout --version
+```
+
+## Options
+
+Option      | Type      | Required  | Default | Description
+------------|:---------:|:---------:|:-------:|------------
+`--help`    | `boolean` | no        | -       | Show help
+`--version` | `boolean` | no        | -       | Show version number
+
+## Examples
+
+```bash
+openapi logout
+Logged out from the Redocly account. ✋
+```
\ No newline at end of file
diff --git a/docs/commands/push.md b/docs/commands/push.md
index bd9f4ee0..831e6b2a 100644
--- a/docs/commands/push.md
+++ b/docs/commands/push.md
@@ -49,7 +49,7 @@ The prerequisite for using the `push` command is an active user account in a Red
 
 To find your organization ID required for the command, log into Workflows and access the **API registry** page. In your browser's address bar, find the URL of this page. The segment after `app.redoc.ly/org/` is your organization ID. For example, if the URL is `app.redoc.ly/org/test_docs`, the organization ID is `test_docs`. When using the `push` command, you would provide this ID as `@test_docs`. Note that the organization ID can differ from the organization name. Owners can change the organization name at any time in the Workflows **Org settings** page, but the organization ID cannot be changed.
 
-To authenticate to the API registry, you can use the `REDOCLY_AUTHORIZATION` environment variable. It can be set to either your [personal API key](../../workflows/personal-api-keys.md) or to an organization-wide API key (configurable by organization owners in **Redocly Workflows > Org settings > API keys**).
+To authenticate to the API registry, you can use the `REDOCLY_AUTHORIZATION` environment variable. It can be set to either your [personal API key](../../settings/personal-api-keys.md) or to an organization-wide API key (configurable by organization owners in **Redocly Workflows > Org settings > API keys**).
 
 Treat the API keys as secrets and work with them accordingly. Consult the documentation for your CI system to learn more about handling secrets:
 
diff --git a/docs/configuration/configuration-file.mdx b/docs/configuration/configuration-file.mdx
new file mode 100644
index 00000000..467de4e2
--- /dev/null
+++ b/docs/configuration/configuration-file.mdx
@@ -0,0 +1,85 @@
+import { JsonSchema } from '@redocly/developer-portal/ui';
+import Schema from './schema.yaml';
+import { StyledContent } from '../../components/styled.elements';
+
+
+# Configuration file
+
+## Purpose
+
+The configuration file controls configuration options of the CLI tool.
+It may be passed as metadata to the API registry which can then subsequently be used to build the docs.
+
+## Filename and location
+
+The CLI tool will search the current working directory for a configuration file named `.redocly.yaml`.
+
+## File format
+
+The configuration file is expected to be in YAML format.
+
+The contents of the YAML are according to the following structure:
+
+```yaml
+apiDefinitions:
+# a set of named keys and values of entrypoints to OpenAPI 3 definitions.
+# example
+  petstore: ./openapi/openapi.yaml
+  caretaker: ./openapi/caretaker.yaml
+
+lint:
+# the lint options
+
+referenceDocs:
+# the API reference docs options, including theme options.
+
+registry:
+# the API registry options
+
+# future products may be configured by extending this file
+```
+
+### apiDefinitions
+
+The `apiDefinitions` section's purpose is to define one or more entrypoints to your OpenAPI 3 definitions.
+Redocly supports both multi-file definitions and multiple (multi-file) definitions.
+
+This section is optional.
+
+The validation command uses the `apiDefinitions` for providing shortcuts to referencing the definition.
+
+Based on the example `.redocly.yaml` file above, here is an example usage:
+```
+openapi-cli lint petstore caretaker
+```
+
+In addition, it allows for running the command which will apply to all APIs defined:
+```
+openapi-cli lint
+```
+
+### lint
+
+The `lint` section's primary purpose is to define options for the `lint` and `bundle` commands.
+You may also import plugins and extends their configurations.
+
+Read more about the [lint configuration section](lint.md).
+
+### referenceDocs
+
+The `referenceDocs` section configures features (availability and options) and theming (style, fonts, colors).
+
+There are two products: Redoc Community Edition (CE), and Redocly API reference docs. Reference docs supports all configuration options from Redoc Community Edition plus a set of premium options.
+
+Find the full list of supported options on the [Reference docs configuration page](../../api-reference-docs/configuration/index.mdx).
+
+### registry
+
+The `registry` section is reserved for future API registry features.
+
+Read more about the [`registry` section](registry.md).
+
+# Schema reference
+<StyledContent>
+<JsonSchema schema={Schema}/>
+</StyledContent>
diff --git a/docs/configuration/index.mdx b/docs/configuration/index.mdx
index 467de4e2..7920f3b4 100644
--- a/docs/configuration/index.mdx
+++ b/docs/configuration/index.mdx
@@ -1,85 +1,20 @@
-import { JsonSchema } from '@redocly/developer-portal/ui';
-import Schema from './schema.yaml';
-import { StyledContent } from '../../components/styled.elements';
+---
+title: OpenAPI CLI configuration
+description: Learn how to configure OpenAPI CLI
+---
 
+import { WideTile, Flex } from '@redocly/developer-portal/ui';
 
-# Configuration file
+# OpenAPI CLI configuration
 
-## Purpose
+Customize OpenAPI CLI by editing configuration files.
 
-The configuration file controls configuration options of the CLI tool.
-It may be passed as metadata to the API registry which can then subsequently be used to build the docs.
+<div>
 
-## Filename and location
+  <Flex justifyContent="space-between">
+    <WideTile to="./configuration-file.mdx" header="Configuration file">
+      Working with the OpenAPI CLI configuration file
+    </WideTile>
+  </Flex>
+</div>
 
-The CLI tool will search the current working directory for a configuration file named `.redocly.yaml`.
-
-## File format
-
-The configuration file is expected to be in YAML format.
-
-The contents of the YAML are according to the following structure:
-
-```yaml
-apiDefinitions:
-# a set of named keys and values of entrypoints to OpenAPI 3 definitions.
-# example
-  petstore: ./openapi/openapi.yaml
-  caretaker: ./openapi/caretaker.yaml
-
-lint:
-# the lint options
-
-referenceDocs:
-# the API reference docs options, including theme options.
-
-registry:
-# the API registry options
-
-# future products may be configured by extending this file
-```
-
-### apiDefinitions
-
-The `apiDefinitions` section's purpose is to define one or more entrypoints to your OpenAPI 3 definitions.
-Redocly supports both multi-file definitions and multiple (multi-file) definitions.
-
-This section is optional.
-
-The validation command uses the `apiDefinitions` for providing shortcuts to referencing the definition.
-
-Based on the example `.redocly.yaml` file above, here is an example usage:
-```
-openapi-cli lint petstore caretaker
-```
-
-In addition, it allows for running the command which will apply to all APIs defined:
-```
-openapi-cli lint
-```
-
-### lint
-
-The `lint` section's primary purpose is to define options for the `lint` and `bundle` commands.
-You may also import plugins and extends their configurations.
-
-Read more about the [lint configuration section](lint.md).
-
-### referenceDocs
-
-The `referenceDocs` section configures features (availability and options) and theming (style, fonts, colors).
-
-There are two products: Redoc Community Edition (CE), and Redocly API reference docs. Reference docs supports all configuration options from Redoc Community Edition plus a set of premium options.
-
-Find the full list of supported options on the [Reference docs configuration page](../../api-reference-docs/configuration/index.mdx).
-
-### registry
-
-The `registry` section is reserved for future API registry features.
-
-Read more about the [`registry` section](registry.md).
-
-# Schema reference
-<StyledContent>
-<JsonSchema schema={Schema}/>
-</StyledContent>
diff --git a/docs/configuration/lint.md b/docs/configuration/lint.md
index 9d2bd638..ba13451a 100644
--- a/docs/configuration/lint.md
+++ b/docs/configuration/lint.md
@@ -1,6 +1,6 @@
 # Lint configuration
 
-The `lint` configuration section is part of the [Redocly configuration file](index.mdx).
+The `lint` configuration section is part of the [Redocly configuration file](configuration-file.mdx).
 It is used by the `lint` and `bundle` commands to control various options.
 
 Modify (or create) the `.redocly.yaml` file in the directory from which you are going to run the `lint` or `bundle` commands.
@@ -155,7 +155,7 @@ If not defined, it will fall back to the `rules` section.
 If it is version OpenAPI 3.0, it will prioritize the `oas3_0Rules` section.
 If not defined, it will fall back to the `rules` section.
 
-Read about our [built-in rules](../built-in-rules.md).
+Read about our [built-in rules](../resources/built-in-rules.md).
 
 
 ## Resolving JSON references ($refs)
diff --git a/docs/guides/hide-apis.md b/docs/guides/hide-apis.md
index 589e2b75..139b28c0 100644
--- a/docs/guides/hide-apis.md
+++ b/docs/guides/hide-apis.md
@@ -678,7 +678,7 @@ paths:
 
 Redocly's API registry uses [OpenAPI-cli](../../../openapi-cli.mdx) under the hood.
 
-OpenAPI-cli is a very powerful tool that allows you [create custom plugins](../custom-rules.md) to:
+OpenAPI-cli is a very powerful tool that allows you [create custom plugins](../resources/custom-rules.md) to:
 - create your own organization's linting rules
 - transform your API definition during the bundle process (using decorators)
 
@@ -847,10 +847,12 @@ module.exports = {
 };
 ```
 
-<div class="danger">
+:::danger
+
 From a security perspective, explicitly allowing something is typically more secure than explicitly disallowing something.
-Therefore, consider <code>x-external</code> vs. <code>x-internal</code> and adjust the decorators accordingly.
-</div>
+Therefore, consider `x-external` vs. `x-internal` and adjust the decorators accordingly.
+
+:::
 
 The example above covers paths and operations, but it doesn't cover specific schema properties.
 Add another decorator, for example, `remove-internal-schema-properties`, to accomplish that.
diff --git a/docs/guides/index.mdx b/docs/guides/index.mdx
new file mode 100644
index 00000000..dd6211dd
--- /dev/null
+++ b/docs/guides/index.mdx
@@ -0,0 +1,35 @@
+---
+title: OpenAPI CLI guides
+description: Browse how-to guides for working with OpenAPI CLI
+---
+
+import { WideTile, Flex } from '@redocly/developer-portal/ui';
+
+# OpenAPI CLI guides
+
+Learn how to work with OpenAPI CLI.
+
+<div>
+
+  <Flex justifyContent="space-between">
+    <WideTile to="./lint.md" header="Managing the lint configuration">
+      How to configure the lint tool
+    </WideTile>
+    <WideTile to="./response-contains-property.md" header="Enforcing response contents">
+      How to create a custom rule to enforce response contents
+    </WideTile>
+  </Flex>
+</div>
+
+
+<div>
+
+  <Flex justifyContent="space-between">
+    <WideTile to="./hide-apis.md" header="Hiding internal APIs">
+      How to hide internal APIs
+    </WideTile>
+    <WideTile to="./replace-server-url.md" header="Replacing the server URL">
+      How to replace the server URL in different environments
+    </WideTile>
+  </Flex>
+</div>
diff --git a/docs/guides/lint.md b/docs/guides/lint.md
index 467aa77f..d8dccb72 100644
--- a/docs/guides/lint.md
+++ b/docs/guides/lint.md
@@ -4,7 +4,7 @@ redirectFrom:
 ---
 # Lint configuration
 
-The `lint` configuration section is part of the [Redocly configuration file](../configuration/index.mdx).
+The `lint` configuration section is part of the [Redocly configuration file](../configuration/configuration-file.mdx).
 It is used by the `lint` and `bundle` commands to control various options.
 
 Modify (or create) the `.redocly.yaml` file in the directory from which you are going to run the `lint` or `bundle` commands.
@@ -124,10 +124,13 @@ lint:
       severity: error
 ```
 To know which rules support options, please read the built-in rules documentation.
-<div class="success">
+
+:::success Tip
+
 If you write custom rules, you may create rules that accept additional configuration options as well.
 Be sure to document those options for your users.
-</div>
+
+:::
 
 ## Different OpenAPI versions
 
@@ -156,7 +159,7 @@ If not defined, it will fall back to the `rules` section.
 If it is version OpenAPI 3.0, it will prioritize the `oas3_0Rules` section.
 If not defined, it will fall back to the `rules` section.
 
-Read about our [built-in rules](../built-in-rules.md).
+Read about our [built-in rules](../resources/built-in-rules.md).
 
 
 ## Resolving JSON references ($refs)
diff --git a/docs/guides/replace-server-url.md b/docs/guides/replace-server-url.md
index 8960873d..7fd340a8 100644
--- a/docs/guides/replace-server-url.md
+++ b/docs/guides/replace-server-url.md
@@ -4,7 +4,7 @@ redirectFrom:
 ---
 # Replace server URL in different environments
 
-Redocly allows you to use [custom decorators](../../cli/custom-rules.md) to modify content in the API definition during the bundling process.
+Redocly allows you to use [custom decorators](../resources/custom-rules.md) to modify content in the API definition during the bundling process.
 
 This page describes how to replace the server URL with a decorator for a given environment.
 
@@ -33,9 +33,11 @@ module.exports = {
 
 2. Save the `demo-plugin.js` file in a directory called `plugins`.
 
-<div class="attention">
-You can name the plugins directory anything you want. Make sure you use the correct name in the <code>.redocly.yaml</code> file (Step 4).
-</div>
+:::attention
+
+You can name the plugins directory anything you want. Make sure you use the correct name in the `.redocly.yaml` file (Step 4).
+
+:::
 
 ## Step 2: Add a decorator and associate it with an environment variable
 
@@ -66,9 +68,11 @@ function ReplaceServersURL() {
 
 2. Save the `replace-servers-url.js` file in a directory called `decorators`.
 
-<div class="attention">
-You can name the decorators directory anything you want. Make sure you use the correct directory name in the <code>demo-plugin.js</code> file (Step 1).
-</div>
+:::attention
+
+You can name the decorators directory anything you want. Make sure you use the correct directory name in the `demo-plugin.js` file (Step 1).
+
+:::
 
 ## Step 3: Add an environment variable
 
@@ -86,7 +90,7 @@ In this step, add the environment variable `SERVERS_URL` (defined in the previou
 
 ![Environment variables](./images/envt-variable-after.png)
 
-To create API versions for different server URLs, use the **API registry**. [Add another version](../../workflows/api-registry/versions.md) of the API with a different environment variable value.
+To create API versions for different server URLs, use the **API registry**. [Add another version](../../api-registry/guides/versions.md) of the API with a different environment variable value.
 
 ## Step 4: Register the plugin for Redocly use
 
diff --git a/docs/index.md b/docs/index.md
index 685eb536..d19ba265 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -15,7 +15,7 @@ Redocly OpenAPI CLI is an open-source command-line tool used to:
 - Build production-ready reference docs (requires an Enterprise license key).
 
 :::success Tip
-You can also [extend](custom-rules.md) the functionality of Redocly OpenAPI CLI by dynamically adding
+You can also [extend](./resources/custom-rules.md) the functionality of Redocly OpenAPI CLI by dynamically adding
 or removing content during the bundling process using decorators or by defining your own rules to validate OpenAPI definitions
 :::
 
@@ -36,8 +36,6 @@ Currently, Redocly OpenAPI CLI supports these features:
 
 ## Why Redocly OpenAPI CLI?
 
-### Performance
-
 OpenAPI CLI is your all-in-one Swiss army knife when designing, defining, and working with OpenAPI definitions.
 
 There are four pillars that make Redocly OpenAPI CLI great - **performance**, **bundling**, **linting**, and **extensibility**. To better understand why you need to look at the OpenAPI authoring process from a higher level.
diff --git a/docs/quickstart.md b/docs/quickstart.md
index c64777c0..666ceab5 100644
--- a/docs/quickstart.md
+++ b/docs/quickstart.md
@@ -270,9 +270,9 @@ For the warning:
 * Operation object should contain `operationId` field (there should be a particular field here)
 * Warning was generated by the operation-operationId rule (a rule that detected the warning)
 
-You got that error because you've changed a property that is strictly defined in the specification. The linter has the [`spec`](./built-in-rules.md#spec) built-in rule that generates an error each time when it finds something that is not defined in the specification.
+You got that error because you've changed a property that is strictly defined in the specification. The linter has the [`spec`](./resources/built-in-rules.md#spec) built-in rule that generates an error each time when it finds something that is not defined in the specification.
 
-The warning is a consequence of the error - when you changed the `operationId`, you also changed the object that this property is a part of - the [Operation object](https://spec.openapis.org/oas/latest.html#operation-object). Since the `operationId` is no longer there, you get a warning triggered by the corresponding built-in rule - [operation-operationId](./built-in-rules.md#operation-operationid).
+The warning is a consequence of the error - when you changed the `operationId`, you also changed the object that this property is a part of - the [Operation object](https://spec.openapis.org/oas/latest.html#operation-object). Since the `operationId` is no longer there, you get a warning triggered by the corresponding built-in rule - [operation-operationId](./resources/built-in-rules.md#operation-operationid).
 
 Let's make your definition valid again. Look at the defined Field Names of the [Operation object](https://spec.openapis.org/oas/latest.html#operation-object) (after the warning description). Detect what's wrong with your definition file - there cannot be a Field Name with the `operationIdentifier` name (it is invalid). Change it back to the `operationId` and you're done. You will fix both the warning and the error because the unexpected property `operationIdentifier` is gone.
 
@@ -424,5 +424,5 @@ Navigate to `http://127.0.0.1:8080` and check the updated definition file.
 ## Next steps
 
 - Check the full list of [OpenAPI CLI commands](./commands/index.md) available to you
-- Learn how to fine-tune your OpenAPI CLI tool using a [configuraton file](./configuration/index.mdx)
-- Learn more about [custom plugins and rules](./custom-rules.md)
+- Learn how to fine-tune your OpenAPI CLI tool using a [configuration file](./configuration/configuration-file.mdx)
+- Learn more about [custom plugins and rules](./resources/custom-rules.md)
diff --git a/docs/built-in-rules.md b/docs/resources/built-in-rules.md
similarity index 98%
rename from docs/built-in-rules.md
rename to docs/resources/built-in-rules.md
index 7cc9fc55..87f343a3 100644
--- a/docs/built-in-rules.md
+++ b/docs/resources/built-in-rules.md
@@ -1,3 +1,9 @@
+---
+title: Built-in rules in OpenAPI CLI
+redirectFrom:
+  - /docs/cli/built-in-rules/
+---
+
 # Built in rules
 
 All of our built-in rules are listed below.
diff --git a/docs/custom-rules.md b/docs/resources/custom-rules.md
similarity index 97%
rename from docs/custom-rules.md
rename to docs/resources/custom-rules.md
index 45876aa3..aba93543 100644
--- a/docs/custom-rules.md
+++ b/docs/resources/custom-rules.md
@@ -1,3 +1,9 @@
+---
+title: Custom plugins and rules in OpenAPI CLI
+redirectFrom:
+  - /docs/cli/custom-rules/
+---
+
 # Custom plugins and rules
 
 ## Concepts
@@ -74,7 +80,7 @@ function OperationIdNotTest() {
 }
 ```
 
-See an example of a custom rule implementation in our ["Response contains property" custom rule](./guides/response-contains-property.md).
+See an example of a custom rule implementation in our ["Response contains property" custom rule](../guides/response-contains-property.md).
 
 
 ## Format of visitor
@@ -82,6 +88,7 @@ See an example of a custom rule implementation in our ["Response contains proper
 Keys of the object can be any of the following:
 
 - node type - visitor will be called on specific node type. List of available node types for each specific OAS version:
+  - OAS3.1: https://github.com/Redocly/openapi-cli/blob/master/packages/core/src/types/oas3_1.ts#L209
   - OAS3: https://github.com/Redocly/openapi-cli/blob/master/packages/core/src/types/oas3.ts#L530
   - OAS2: https://github.com/Redocly/openapi-cli/blob/master/packages/core/src/types/oas2.ts#L367
 - `any` - visitor will be called on every node
@@ -304,7 +311,7 @@ module.exports = {
 }
 ```
 
-See an example of a custom decorator implementation in our [how-to hide APIs guide](./guides/hide-apis.md).
+See an example of a custom decorator implementation in our [how-to hide APIs guide](../guides/hide-apis.md).
 
 ## Configs in plugins
 
diff --git a/docs/resources/index.mdx b/docs/resources/index.mdx
new file mode 100644
index 00000000..476eef5d
--- /dev/null
+++ b/docs/resources/index.mdx
@@ -0,0 +1,22 @@
+---
+title: OpenAPI CLI resources
+description: Learn more about how OpenAPI CLI works
+---
+
+import { WideTile, Flex } from '@redocly/developer-portal/ui';
+
+# OpenAPI CLI resources
+
+Additional information on concepts, features, and functionalities related to OpenAPI CLI.
+
+<div>
+
+  <Flex justifyContent="space-between">
+    <WideTile to="./built-in-rules.md" header="Built-in lint rules">
+      List of built-in lint rules provided with OpenAPI CLI
+    </WideTile>
+    <WideTile to="./custom-rules.md" header="Custom plugins and rules">
+      Guidelines for writing your own plugins and rules
+    </WideTile>
+  </Flex>
+</div>
diff --git a/package-lock.json b/package-lock.json
index b092d039..267290ab 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "@redocly/openapi",
-  "version": "1.0.0-beta.55",
+  "version": "1.0.0-beta.58",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "@redocly/openapi",
-      "version": "1.0.0-beta.55",
+      "version": "1.0.0-beta.58",
       "license": "MIT",
       "workspaces": [
         "packages/*"
@@ -9631,7 +9631,7 @@
     },
     "packages/cli": {
       "name": "@redocly/openapi-cli",
-      "version": "1.0.0-beta.55",
+      "version": "1.0.0-beta.58",
       "license": "MIT",
       "dependencies": {
         "@redocly/openapi-core": "^1.0.0-beta.55",
@@ -9725,7 +9725,7 @@
     },
     "packages/core": {
       "name": "@redocly/openapi-core",
-      "version": "1.0.0-beta.55",
+      "version": "1.0.0-beta.58",
       "license": "MIT",
       "dependencies": {
         "@redocly/ajv": "^8.6.2",
diff --git a/package.json b/package.json
index 2cc4d9ea..c0e6210a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@redocly/openapi",
-  "version": "1.0.0-beta.55",
+  "version": "1.0.0-beta.58",
   "description": "",
   "private": true,
   "engines": {
@@ -85,4 +85,4 @@
       }
     }
   }
-}
\ No newline at end of file
+}
diff --git a/packages/cli/package.json b/packages/cli/package.json
index f90c99b1..8e09ece1 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@redocly/openapi-cli",
-  "version": "1.0.0-beta.55",
+  "version": "1.0.0-beta.58",
   "description": "",
   "license": "MIT",
   "bin": {
@@ -51,4 +51,4 @@
     "@types/yargs": "16.0.2",
     "typescript": "^4.0.3"
   }
-}
\ No newline at end of file
+}
diff --git a/packages/cli/src/commands/preview-docs/preview-server/preview-server.ts b/packages/cli/src/commands/preview-docs/preview-server/preview-server.ts
index 68f47abc..d0bf4623 100644
--- a/packages/cli/src/commands/preview-docs/preview-server/preview-server.ts
+++ b/packages/cli/src/commands/preview-docs/preview-server/preview-server.ts
@@ -6,6 +6,7 @@ import * as path from 'path';
 
 import { startHttpServer, startWsServer, respondWithGzip, mimeTypes } from './server';
 import type { IncomingMessage } from 'http';
+import { isSubdir } from '../../../utils';
 
 function getPageHTML(
   htmlTemplate: string,
@@ -104,6 +105,12 @@ export default async function startPreviewServer(
         }[request.url || ''] ||
         path.resolve(htmlTemplate ? path.dirname(htmlTemplate) : process.cwd(), `.${request.url}`);
 
+      if (!isSubdir(process.cwd(), filePath)) {
+        respondWithGzip('404 Not Found', request, response, { 'Content-Type': 'text/html' }, 404);
+        console.timeEnd(colorette.dim(`GET ${request.url}`));
+        return;
+      }
+
       const extname = String(path.extname(filePath)).toLowerCase() as keyof typeof mimeTypes;
 
       const contentType = mimeTypes[extname] || 'application/octet-stream';
diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts
index f17834aa..5a35e9a8 100644
--- a/packages/cli/src/utils.ts
+++ b/packages/cli/src/utils.ts
@@ -290,4 +290,12 @@ export function slash(path: string): string {
   }
 
   return path.replace(/\\/g, '/');
+}
+
+/**
+ * Checks if dir is subdir of parent
+ */
+export function isSubdir(parent: string, dir: string): boolean {
+  const relative = path.relative(parent, dir);
+  return !!relative && !/^..($|\/)/.test(relative) && !path.isAbsolute(relative);
 }
\ No newline at end of file
diff --git a/packages/core/package.json b/packages/core/package.json
index 2995e98e..47f333da 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@redocly/openapi-core",
-  "version": "1.0.0-beta.55",
+  "version": "1.0.0-beta.58",
   "description": "",
   "main": "lib/index.js",
   "engines": {
@@ -48,4 +48,4 @@
     "@types/node-fetch": "^2.5.7",
     "typescript": "^4.0.5"
   }
-}
\ No newline at end of file
+}
diff --git a/packages/core/src/bundle.ts b/packages/core/src/bundle.ts
index 2656ff5b..f80fc642 100755
--- a/packages/core/src/bundle.ts
+++ b/packages/core/src/bundle.ts
@@ -114,6 +114,7 @@ export async function bundleDocument(opts: {
     bundle: document,
     problems: ctx.problems.map((problem) => config.addProblemToIgnore(problem)),
     fileDependencies: externalRefResolver.getFiles(),
+    rootType: types.DefinitionRoot,
     refTypes: ctx.refTypes,
   };
 }
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index a7fb6048..e5ee032c 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -2,6 +2,7 @@ export { BundleOutputFormat, readFileFromUrl } from './utils';
 export { Oas3_1Types } from './types/oas3_1';
 export { Oas3Types } from './types/oas3';
 export { Oas2Types } from './types/oas2';
+export { ConfigTypes } from './types/redocly-yaml';
 export {
   Oas3Definition,
   Oas3Components,
@@ -43,5 +44,11 @@ export {
 
 export { getAstNodeByPointer, getLineColLocation } from './format/codeframes';
 export { formatProblems, OutputFormat, getTotals, Totals } from './format/format';
-export { lint, lint as validate, lintDocument, lintFromString } from './lint';
+export {
+  lint,
+  lint as validate,
+  lintDocument,
+  lintFromString,
+  lintConfig,
+} from './lint';
 export { bundle, bundleDocument } from './bundle';
diff --git a/packages/core/src/lint.ts b/packages/core/src/lint.ts
index 3555097a..832a7359 100755
--- a/packages/core/src/lint.ts
+++ b/packages/core/src/lint.ts
@@ -6,12 +6,15 @@ import { Oas3_1Types } from './types/oas3_1';
 import { Oas3Types } from './types/oas3';
 import { Oas2Types } from './types/oas2';
 import { NodeType } from './types';
-import { WalkContext, walkDocument } from './walk';
+import { ProblemSeverity, WalkContext, walkDocument } from './walk';
 import { LintConfig, Config } from './config/config';
 import { normalizeTypes } from './types';
 import { initRules } from './config/rules';
 import { releaseAjvInstance } from './rules/ajv';
 import { detectOpenAPI, OasMajorVersion, OasVersion, openAPIMajor } from './oas-types';
+import { ConfigTypes } from './types/redocly-yaml';
+import { OasSpec } from './rules/common/spec';
+import { defaultPlugin } from './config/builtIn';
 
 export async function lint(opts: {
   ref: string;
@@ -89,3 +92,33 @@ export async function lintDocument(opts: {
   });
   return ctx.problems.map((problem) => config.addProblemToIgnore(problem));
 }
+
+export async function lintConfig(opts: {
+  document: Document,
+}) {
+  const { document } = opts;
+
+  const ctx: WalkContext = {
+    problems: [],
+    oasVersion: OasVersion.Version3_0,
+  };
+  const config = new LintConfig({
+    plugins: [defaultPlugin],
+    extends: [],
+    rules: { spec: 'error' },
+  });
+
+  const types = normalizeTypes(ConfigTypes, config);
+  const rules = [{ severity: 'error' as ProblemSeverity, ruleId: 'spec', visitor: OasSpec({ severity: 'error' }) }];
+  const normalizedVisitors = normalizeVisitors(rules, types);
+
+  walkDocument({
+    document,
+    rootType: types.ConfigRoot,
+    normalizedVisitors,
+    resolvedRefMap: new Map(),
+    ctx,
+  });
+
+  return ctx.problems;
+}
diff --git a/packages/core/src/redocly/index.ts b/packages/core/src/redocly/index.ts
index 111f59e2..f8158708 100644
--- a/packages/core/src/redocly/index.ts
+++ b/packages/core/src/redocly/index.ts
@@ -93,7 +93,7 @@ export class RedoclyClient {
 
   static async authorize(accessToken: string, verbose: boolean = false) {
     try {
-      const queryStr = `{ definitions { id } }`;
+      const queryStr = `{ viewer { id } }`;
 
       return await query(queryStr, {}, { Authorization: accessToken });
     } catch (e) {
@@ -147,7 +147,7 @@ export class RedoclyClient {
           __typename
         }
       }
-  
+
       fragment VersionDetails on DefinitionVersion {
         id
         nodeId
diff --git a/packages/core/src/rules/common/no-ambiguous-paths.ts b/packages/core/src/rules/common/no-ambiguous-paths.ts
index 3bcdf827..89c7dec5 100644
--- a/packages/core/src/rules/common/no-ambiguous-paths.ts
+++ b/packages/core/src/rules/common/no-ambiguous-paths.ts
@@ -6,7 +6,7 @@ import { Oas2Paths } from '../../typings/swagger';
 export const NoAmbiguousPaths: Oas3Rule | Oas2Rule = () => {
   return {
     PathMap(pathMap: Oas3Paths | Oas2Paths, { report, location }: UserContext) {
-      const seenPaths = [];
+      const seenPaths: string[] = [];
 
       for (const currentPath of Object.keys(pathMap)) {
         const ambiguousPath = seenPaths.find((seenPath) =>
diff --git a/packages/core/src/rules/utils.ts b/packages/core/src/rules/utils.ts
index f63c45ff..1b1f1347 100644
--- a/packages/core/src/rules/utils.ts
+++ b/packages/core/src/rules/utils.ts
@@ -53,6 +53,7 @@ export function validateDefinedAndNonEmpty(fieldName: string, value: any, ctx: U
   if (value[fieldName] === undefined) {
     ctx.report({
       message: missingRequiredField(ctx.type.name, fieldName),
+      location: ctx.location.child([fieldName]).key(),
     });
   } else if (!value[fieldName]) {
     ctx.report({
diff --git a/packages/core/src/types/redocly-yaml.ts b/packages/core/src/types/redocly-yaml.ts
new file mode 100644
index 00000000..400b3184
--- /dev/null
+++ b/packages/core/src/types/redocly-yaml.ts
@@ -0,0 +1,195 @@
+import { NodeType, listOf } from '.';
+
+const ConfigRoot: NodeType = {
+  properties: {
+    apiDefinitions: {
+      type: 'object',
+      properties: {},
+      additionalProperties: { properties: { type: 'string' } }
+    },
+    lint: 'ConfigLint',
+    referenceDocs: 'ConfigReferenceDocs',
+  },
+};
+
+const ConfigHTTP: NodeType = {
+  properties: {
+    headers: {
+      type: 'array',
+      items: {
+        type: 'string',
+      },
+    },
+  },
+};
+
+const ConfigLint: NodeType = {
+  properties: {
+    plugins: {
+      type: 'array',
+      items: { type: 'string' },
+    },
+    extends: {
+      type: 'array',
+      items: {
+        type: 'string',
+      },
+    },
+    doNotResolveExamples: { type: 'boolean' },
+    rules: { type: 'object' },
+    oas2Rules: { type: 'object' },
+    oas3_0Rules: { type: 'object' },
+    oas3_1Rules: { type: 'object' },
+    preprocessors: { type: 'object' },
+    oas2Preprocessors: { type: 'object' },
+    oas3_0Preprocessors: { type: 'object' },
+    oas3_1Preprocessors: { type: 'object' },
+    decorators: { type: 'object' },
+    oas2Decorators: { type: 'object' },
+    oas3_0Decorators: { type: 'object' },
+    oas3_1Decorators: { type: 'object' },
+    resolve: {
+      properties: {
+        http: 'ConfigHTTP',
+      },
+    },
+  },
+};
+
+const ConfigLanguage: NodeType = {
+  properties: {
+    label: { type: 'string' },
+    lang: { type: 'string' },
+  },
+};
+
+const ConfigLabels: NodeType = {
+  properties: {
+    enum: { type: 'string' },
+    enumSingleValue: { type: 'string' },
+    enumArray: { type: 'string' },
+    default: { type: 'string' },
+    deprecated: { type: 'string' },
+    example: { type: 'string' },
+    examples: { type: 'string' },
+    nullable: { type: 'string' },
+    recursive: { type: 'string' },
+    arrayOf: { type: 'string' },
+    webhook: { type: 'string' },
+    authorizations: { type: 'string' },
+    tryItAuthBasicUsername: { type: 'string' },
+    tryItAuthBasicPassword: { type: 'string' },
+  },
+};
+
+const ConfigSidebarLinks: NodeType = {
+  properties: {
+    placement: { type: 'string' },
+    label: { type: 'string' },
+    link: { type: 'string' },
+    target: { type: 'string' },
+  },
+};
+
+const ConfigTheme: NodeType = {
+  properties: {
+    breakpoints: { type: 'object', additionalProperties: { type: 'string' } },
+    codeBlock: { type: 'object', additionalProperties: { type: 'string' } },
+    colors: { type: 'object', additionalProperties: { type: 'string' } },
+    components: { type: 'object', additionalProperties: { type: 'string' } },
+    layout: { type: 'object', additionalProperties: { type: 'string' } },
+    logo: { type: 'object', additionalProperties: { type: 'string' } },
+    overrides: { type: 'object', additionalProperties: { type: 'string' } },
+    rightPanel: { type: 'object', additionalProperties: { type: 'string' } },
+    schema: { type: 'object', additionalProperties: { type: 'string' } },
+    shape: { type: 'object', additionalProperties: { type: 'string' } },
+    sidebar: { type: 'object', additionalProperties: { type: 'string' } },
+    spacing: { type: 'object', additionalProperties: { type: 'string' } },
+    typography: { type: 'object', additionalProperties: { type: 'string' } },
+    links: { properties: { color: { type: 'string' } } },
+    codeSample: { properties: { backgroundColor: { type: 'string' } } },
+  },
+}
+
+const ConfigReferenceDocs: NodeType = {
+  properties: {
+    theme: 'ConfigTheme',
+    corsProxyUrl: { type: 'string' },
+    ctrlFHijack: { type: 'boolean' },
+    defaultSampleLanguage: { type: 'string' },
+    disableDeepLinks: { type: 'boolean' },
+    disableSearch: { type: 'boolean' },
+    disableSidebar: { type: 'boolean' },
+    downloadDefinitionUrl: { type: 'string' },
+    expandDefaultServerVariables: { type: 'boolean' },
+    expandResponses: { type: 'string' },
+    expandSingleSchemaField: { type: 'boolean' },
+    generateCodeSamples: {
+      properties: {
+        skipOptionalParameters: { type: 'boolean' },
+        languages: listOf('ConfigLanguage'),
+      },
+    },
+    generatedPayloadSamplesMaxDepth: { type: 'number' },
+    hideDownloadButton: { type: 'boolean' },
+    hideHostname: { type: 'boolean' },
+    hideInfoSection: { type: 'boolean' },
+    hideLoading: { type: 'boolean' },
+    hideLogo: { type: 'boolean' },
+    hideRequestPayloadSample: { type: 'boolean' },
+    hideSchemaPattern: { type: 'boolean' },
+    hideSchemaTitles: { type: 'boolean' },
+    hideSingleRequestSampleTab: { type: 'boolean' },
+    htmlTemplate: { type: 'string' },
+    jsonSampleExpandLevel: { type: 'string' },
+    labels: 'ConfigLabels',
+    layout: { type: 'object' },
+    maxDisplayedEnumValues: { type: 'number' },
+    menuToggle: { type: 'boolean' },
+    nativeScrollbars: { type: 'boolean' },
+    noAutoAuth: { type: 'boolean' },
+    oAuth2RedirectURI: { type: 'string' },
+    onDeepLinkClick: { type: 'object' },
+    onlyRequiredInSamples: { type: 'boolean' },
+    pagination: { type: 'string' },
+    pathInMiddlePanel: { type: 'boolean' },
+    payloadSampleIdx: { type: 'number' },
+    requestInterceptor: { type: 'object' },
+    requiredPropsFirst: { type: 'boolean' },
+    routingBasePath: { type: 'string' },
+    samplesTabsMaxCount: { type: 'number' },
+    schemaExpansionLevel: { type: 'string' },
+    scrollYOffset: { type: 'string' },
+    searchAutoExpand: { type: 'boolean' },
+    searchFieldLevelBoost: { type: 'number' },
+    searchMode: { type: 'string' },
+    searchOperationTitleBoost: { type: 'number' },
+    searchTagTitleBoost: { type: 'number' },
+    showChangeLayoutButton: { type: 'boolean' },
+    showConsole: { type: 'boolean' },
+    showExtensions: { type: 'boolean' },
+    showNextButton: { type: 'boolean' },
+    showRightPanelToggle: { type: 'boolean' },
+    sidebarLinks: 'ConfigSidebarLinks',
+    sideNavStyle: { type: 'string' },
+    simpleOneOfTypeLabel: { type: 'boolean' },
+    sortEnumValuesAlphabetically: { type: 'boolean' },
+    sortOperationsAlphabetically: { type: 'boolean' },
+    sortPropsAlphabetically: { type: 'boolean' },
+    sortTagsAlphabetically: { type: 'boolean' },
+    unstable_ignoreMimeParameters: { type: 'boolean' },
+    untrustedDefinition: { type: 'boolean' },
+  },
+  additionalProperties: { type: 'string' },
+};
+
+export const ConfigTypes: Record<string, NodeType> = {
+  ConfigRoot,
+  ConfigLint,
+  ConfigReferenceDocs,
+  ConfigHTTP,
+  ConfigLanguage,
+  ConfigLabels,
+  ConfigSidebarLinks,
+  ConfigTheme,
+};


Test patch:
diff --git a/__tests__/info-description-not-present-warning/snapshot.js b/__tests__/info-description-not-present-warning/snapshot.js
index 07adaa67..b2158130 100644
--- a/__tests__/info-description-not-present-warning/snapshot.js
+++ b/__tests__/info-description-not-present-warning/snapshot.js
@@ -3,18 +3,15 @@
 exports[`E2E info-description-not-present-warning 1`] = `
 
 validating /openapi.yaml...
-[1] openapi.yaml:3:3 at #/info
+[1] openapi.yaml:2:1 at #/info/description
 
 Info object should contain \`description\` field.
 
 1 | openapi: 3.0.0
 2 | info:
+  | ^^^^
 3 |   title: Example OpenAPI 3 definition.
-  |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 4 |   version: 1.0.0
-  |   ^^^^^^^^^^^^^^
-5 |
-6 | paths:
 
 Warning was generated by the info-description rule.
 
diff --git a/__tests__/info-license-url-missing-warning/snapshot.js b/__tests__/info-license-url-missing-warning/snapshot.js
index e46277e5..6b2138a2 100644
--- a/__tests__/info-license-url-missing-warning/snapshot.js
+++ b/__tests__/info-license-url-missing-warning/snapshot.js
@@ -3,16 +3,16 @@
 exports[`E2E info-license-url-missing-warning 1`] = `
 
 validating /openapi.yaml...
-[1] openapi.yaml:6:5 at #/info/license
+[1] openapi.yaml:5:3 at #/info/license/url
 
 License object should contain \`url\` field.
 
-4 |   version: 1.0
-5 |   license:
-6 |     name: Apache 2.0
-  |     ^^^^^^^^^^^^^^^^
+3 | title: Example OpenAPI 3 definition.
+4 | version: 1.0
+5 | license:
+  | ^^^^^^^
+6 |   name: Apache 2.0
 7 |
-8 | paths:
 
 Warning was generated by the info-license-url rule.
 
diff --git a/__tests__/operation-description-warning/snapshot.js b/__tests__/operation-description-warning/snapshot.js
index 94eba61d..32d9a7f1 100644
--- a/__tests__/operation-description-warning/snapshot.js
+++ b/__tests__/operation-description-warning/snapshot.js
@@ -3,20 +3,16 @@
 exports[`E2E operation-description-warning 1`] = `
 
 validating /openapi.yaml...
-[1] openapi.yaml:9:7 at #/paths/~1ping/get
+[1] openapi.yaml:8:5 at #/paths/~1ping/get/description
 
 Operation object should contain \`description\` field.
 
- 7 | '/ping':
- 8 |   get:
- 9 |     operationId: gitPing
-   |     ^^^^^^^^^^^^^^^^^^^^
-10 |     summary: example text
-   |     ^^^^^^^^^^^^^^^^^^^^^
- … |     < 2 more lines >
-13 |         description: example description
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-14 |
+ 6 | paths:
+ 7 |   '/ping':
+ 8 |     get:
+   |     ^^^
+ 9 |       operationId: gitPing
+10 |       summary: example text
 
 Warning was generated by the operation-description rule.
 
diff --git a/__tests__/turn-on-all-rules/snapshot.js b/__tests__/turn-on-all-rules/snapshot.js
index 9ef2be1e..e27fff80 100644
--- a/__tests__/turn-on-all-rules/snapshot.js
+++ b/__tests__/turn-on-all-rules/snapshot.js
@@ -19,18 +19,15 @@ Servers must be present.
 Error was generated by the no-empty-servers rule.
 
 
-[2] openapi.yaml:3:3 at #/info
+[2] openapi.yaml:2:1 at #/info/description
 
 Info object should contain \`description\` field.
 
 1 | openapi: 3.0.0
 2 | info:
+  | ^^^^
 3 |   title: Example OpenAPI 3 definition.
-  |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 4 |   version: 1.0.0
-  |   ^^^^^^^^^^^^^^
-5 |
-6 | paths:
 
 Error was generated by the info-description rule.
 
@@ -64,56 +61,44 @@ Info object should contain \`license\` field.
 Error was generated by the info-license rule.
 
 
-[5] openapi.yaml:9:7 at #/paths/~1ping~1{id}~1{test}/get
+[5] openapi.yaml:8:5 at #/paths/~1ping~1{id}~1{test}/get/operationId
 
 Operation object should contain \`operationId\` field.
 
- 7 | '/ping/{id}/{test}':
- 8 |   get:
- 9 |     parameters:
-   |     ^^^^^^^^^^^
-10 |       - in: path
-   |       ^^^^^^^^^^
- … |       < 7 more lines >
-18 |         description: example description
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-19 |
+ 6 | paths:
+ 7 |   '/ping/{id}/{test}':
+ 8 |     get:
+   |     ^^^
+ 9 |       parameters:
+10 |         - in: path
 
 Error was generated by the operation-operationId rule.
 
 
-[6] openapi.yaml:9:7 at #/paths/~1ping~1{id}~1{test}/get
+[6] openapi.yaml:8:5 at #/paths/~1ping~1{id}~1{test}/get/summary
 
 Operation object should contain \`summary\` field.
 
- 7 | '/ping/{id}/{test}':
- 8 |   get:
- 9 |     parameters:
-   |     ^^^^^^^^^^^
-10 |       - in: path
-   |       ^^^^^^^^^^
- … |       < 7 more lines >
-18 |         description: example description
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-19 |
+ 6 | paths:
+ 7 |   '/ping/{id}/{test}':
+ 8 |     get:
+   |     ^^^
+ 9 |       parameters:
+10 |         - in: path
 
 Error was generated by the operation-summary rule.
 
 
-[7] openapi.yaml:9:7 at #/paths/~1ping~1{id}~1{test}/get
+[7] openapi.yaml:8:5 at #/paths/~1ping~1{id}~1{test}/get/description
 
 Operation object should contain \`description\` field.
 
- 7 | '/ping/{id}/{test}':
- 8 |   get:
- 9 |     parameters:
-   |     ^^^^^^^^^^^
-10 |       - in: path
-   |       ^^^^^^^^^^
- … |       < 7 more lines >
-18 |         description: example description
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-19 |
+ 6 | paths:
+ 7 |   '/ping/{id}/{test}':
+ 8 |     get:
+   |     ^^^
+ 9 |       parameters:
+10 |         - in: path
 
 Error was generated by the operation-description rule.
 
diff --git a/packages/cli/src/__tests__/utils.test.ts b/packages/cli/src/__tests__/utils.test.ts
index 508185c5..c0f2f2b4 100644
--- a/packages/cli/src/__tests__/utils.test.ts
+++ b/packages/cli/src/__tests__/utils.test.ts
@@ -1,4 +1,7 @@
-import { slash } from '../utils';
+import { isSubdir, slash } from '../utils';
+
+jest.mock("os");
+
 
 describe('slash path', () => {
   it('can correctly slash path', () => {
@@ -17,3 +20,42 @@ describe('slash path', () => {
     expect(slash(extended)).toBe(extended);
   });
 });
+
+describe('isSubdir', () => {
+  it('can correctly determine if subdir', () => {
+    (
+      [
+        ['/foo', '/foo', false],
+        ['/foo', '/bar', false],
+        ['/foo', '/foobar', false],
+        ['/foo', '/foo/bar', true],
+        ['/foo', '/foo/../bar', false],
+        ['/foo', '/foo/./bar', true],
+        ['/bar/../foo', '/foo/bar', true],
+        ['/foo', './bar', false],
+        ['/foo', '/foo/..bar', true],
+      ] as [string, string, boolean][]
+    ).forEach(([parent, child, expectRes]) => {
+      expect(isSubdir(parent, child)).toBe(expectRes);
+    });
+  });
+
+  it('can correctly determine if subdir for windows-based paths', () => {
+    const os = require('os');
+    os.platform.mockImplementation(() => 'win32');
+
+    (
+      [
+        ['C:/Foo', 'C:/Foo/Bar', true],
+        ['C:\\Foo', 'C:\\Bar', false],
+        ['C:\\Foo', 'D:\\Foo\\Bar', false],
+      ] as [string, string, boolean][]
+    ).forEach(([parent, child, expectRes]) => {
+      expect(isSubdir(parent, child)).toBe(expectRes);
+    });
+  });
+
+  afterEach(() => {
+    jest.resetModules()
+  })
+});
diff --git a/packages/core/src/__tests__/lint.test.ts b/packages/core/src/__tests__/lint.test.ts
index 589a8733..0b4f29c3 100644
--- a/packages/core/src/__tests__/lint.test.ts
+++ b/packages/core/src/__tests__/lint.test.ts
@@ -1,8 +1,8 @@
 import { outdent } from 'outdent';
 
-import { lintFromString } from '../lint';
+import { lintFromString, lintConfig } from '../lint';
 import { loadConfig } from '../config/load';
-import { replaceSourceWithRef } from '../../__tests__/utils';
+import { parseYamlToDocument, replaceSourceWithRef } from '../../__tests__/utils';
 
 describe('lint', () => {
   it('lintFromString should work', async () => {
@@ -41,4 +41,53 @@ describe('lint', () => {
       ]
     `);
   });
+
+  it('lintConfig should work', async () => {
+    const document = parseYamlToDocument(
+      outdent`
+      apiDefinitions: error string
+      lint:
+        plugins:
+          - './local-plugin.js'
+        extends:
+          - recommended
+          - local/all
+        rules:
+          operation-2xx-response: warn
+          no-invalid-media-type-examples: error
+          path-http-verbs-order: error
+          boolean-parameter-prefixes: off
+      referenceDocs:
+        showConsole: true
+        layout:
+          scope: section
+        routingStrategy: browser
+        theme:
+          rightPanel:
+            backgroundColor: '#263238'
+          links:
+            color: '#6CC496'
+      `,
+      '',
+    );
+    const results = await lintConfig({ document });
+
+    expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "location": Array [
+            Object {
+              "pointer": "#/apiDefinitions",
+              "reportOnKey": false,
+              "source": "",
+            },
+          ],
+          "message": "Expected type \`object\` but got \`string\`.",
+          "ruleId": "spec",
+          "severity": "error",
+          "suggest": Array [],
+        },
+      ]
+    `);
+  });
 });
diff --git a/packages/core/src/rules/common/__tests__/info-description.test.ts b/packages/core/src/rules/common/__tests__/info-description.test.ts
index 51379418..7f1f4f9d 100644
--- a/packages/core/src/rules/common/__tests__/info-description.test.ts
+++ b/packages/core/src/rules/common/__tests__/info-description.test.ts
@@ -29,8 +29,8 @@ describe('Oas3 info-description', () => {
         Object {
           "location": Array [
             Object {
-              "pointer": "#/info",
-              "reportOnKey": false,
+              "pointer": "#/info/description",
+              "reportOnKey": true,
               "source": "foobar.yaml",
             },
           ],
diff --git a/packages/core/src/rules/common/__tests__/license-url.test.ts b/packages/core/src/rules/common/__tests__/license-url.test.ts
index 830e46c1..1d46bf8d 100644
--- a/packages/core/src/rules/common/__tests__/license-url.test.ts
+++ b/packages/core/src/rules/common/__tests__/license-url.test.ts
@@ -27,8 +27,8 @@ describe('Oas3 license-url', () => {
         Object {
           "location": Array [
             Object {
-              "pointer": "#/info/license",
-              "reportOnKey": false,
+              "pointer": "#/info/license/url",
+              "reportOnKey": true,
               "source": "foobar.yaml",
             },
           ],
diff --git a/packages/core/src/rules/common/__tests__/tag-description.test.ts b/packages/core/src/rules/common/__tests__/tag-description.test.ts
index e04c5336..bd3be8a3 100644
--- a/packages/core/src/rules/common/__tests__/tag-description.test.ts
+++ b/packages/core/src/rules/common/__tests__/tag-description.test.ts
@@ -28,8 +28,8 @@ describe('Oas3 tag-description', () => {
         Object {
           "location": Array [
             Object {
-              "pointer": "#/tags/0",
-              "reportOnKey": false,
+              "pointer": "#/tags/0/description",
+              "reportOnKey": true,
               "source": "foobar.yaml",
             },
           ],