Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/security-scopes-defined-rule.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@redocly/openapi-core': minor
'@redocly/cli': minor
---

Added a new built-in rule `security-scopes-defined` that requires every scope used in a security requirement to be defined in the corresponding OAuth2 security scheme.
The rule supports OpenAPI 2.0/3.x and AsyncAPI 2.6/3.0, suggests the closest defined scope for typos, and has an opt-in `requireScopes` option that requires OAuth2 security requirements to list at least one scope.
1 change: 1 addition & 0 deletions docs/@v2/rules/built-in-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ The rules list is split into sections.
- [no-unused-components](./oas/no-unused-components.md): All components must be used
- [nullable-type-sibling](./oas/nullable-type-sibling.md): `nullable` must be used with a `type`
- [security-defined](./oas/security-defined.md): Security rules must be defined, either globally or per-operation
- [security-scopes-defined](./common/security-scopes-defined.md): Scopes used in security requirements must be defined in the corresponding OAuth2 security scheme
- [struct](./common/struct.md): Conform to the declared OpenAPI specification version
- [spec-components-invalid-map-name](./oas/spec-components-invalid-map-name.md): Use only alphanumeric and basic punctuation as key names in the components section
- [spec-querystring-parameters](./oas/spec-querystring-parameters.md): Enforce valid use of `in: querystring` (OpenAPI 3.2): at most one per path/operation, and not mixed with `in: query`
Expand Down
116 changes: 116 additions & 0 deletions docs/@v2/rules/common/security-scopes-defined.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
---
slug: /docs/cli/rules/common/security-scopes-defined
---

# security-scopes-defined

Requires that every scope used in a security requirement is defined in the corresponding OAuth2 security scheme.

| OAS | Compatibility |
| --- | ------------- |
| 2.0 | ✅ |
| 3.0 | ✅ |
| 3.1 | ✅ |
| 3.2 | ✅ |

| AsyncAPI | Compatibility |
| -------- | ------------- |
| 2.6 | ✅ |
| 3.0 | ✅ |

The rule checks security schemes of type `oauth2`, where the set of valid scopes is declared in the API description:

- **OpenAPI 3.x and AsyncAPI 2.6**: scopes used in security requirements must be declared in the `scopes` of at least one of the scheme's `flows`.
- **OpenAPI 2.0**: scopes used in security requirements must be declared in the `scopes` of the scheme.
- **AsyncAPI 3.0**: scopes listed in the `scopes` of a security scheme must be declared in the `availableScopes` of at least one of the scheme's `flows`.

Other scheme types are skipped: `openIdConnect` scopes are defined behind the discovery URL and can't be checked statically.
For the remaining types OpenAPI 3.1 and later allow arbitrary role names.
Requirements that reference undefined security schemes are skipped as well — those are reported by the [security-defined](../oas/security-defined.md) rule.

## API design principles

A scope that is used in a security requirement but not declared in the security scheme is almost always a typo or a leftover from a renamed scope.
Clients generated or configured from such a description request permissions that don't exist, and fail at authorization time.
This rule catches the mismatch early and suggests the closest declared scope.

## Configuration

| Option | Type | Description |
| ------------- | ------- | ----------------------------------------------------------------------------------------- |
| severity | string | Possible values: `off`, `warn`, `error`. Default `warn` (in `recommended` configuration). |
| requireScopes | boolean | Requires every `oauth2` security requirement to list at least one scope. Default `false`. |

An example configuration:

```yaml
rules:
security-scopes-defined:
severity: error
requireScopes: true
```

## Examples

Given this configuration:

```yaml
rules:
security-scopes-defined: error
```

Example of an **incorrect** security requirement — the `read:pet` scope is not defined in the scheme:

```yaml
paths:
/pets:
get:
security:
- petstore_auth:
- read:pet
components:
securitySchemes:
petstore_auth:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://example.com/authorize
tokenUrl: https://example.com/token
scopes:
read:pets: Read pets
write:pets: Write pets
```

Example of a **correct** security requirement:

```yaml
paths:
/pets:
get:
security:
- petstore_auth:
- read:pets
components:
securitySchemes:
petstore_auth:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://example.com/authorize
tokenUrl: https://example.com/token
scopes:
read:pets: Read pets
write:pets: Write pets
```

## Related rules

- [security-defined](../oas/security-defined.md)
- [no-unused-components](../oas/no-unused-components.md)
- [configurable rules](../configurable-rules.md)

## Resources

- [Rule source for OpenAPI and AsyncAPI 2.6](https://github.com/Redocly/redocly-cli/blob/main/packages/core/src/rules/common/security-scopes-defined.ts)
- [Rule source for AsyncAPI 3.0](https://github.com/Redocly/redocly-cli/blob/main/packages/core/src/rules/async3/security-scopes-defined.ts)
- [Security scheme docs](https://redocly.com/docs/openapi-visual-reference/security-schemes/)
1 change: 1 addition & 0 deletions docs/@v2/rules/recommended.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Warnings:
- [operation-4xx-response](./oas/operation-4xx-response.md)
- [operation-operationId](./oas/operation-operationId.md)
- [requestBody-replacements-unique](./arazzo/requestBody-replacements-unique.md)
- [security-scopes-defined](./common/security-scopes-defined.md)
- [spec-discriminator-defaultMapping](./oas/spec-discriminator-defaultMapping.md)
- [step-onFailure-unique](./arazzo/step-onFailure-unique.md)
- [step-onSuccess-unique](./arazzo/step-onSuccess-unique.md)
Expand Down
6 changes: 6 additions & 0 deletions docs/@v2/rules/ruleset-templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ rules:
path-parameters-defined: error
path-params-defined: error
security-defined: error
security-scopes-defined: warn
spec-components-invalid-map-name: error
spec-discriminator-defaultMapping: warn
spec-example-values: error
Expand Down Expand Up @@ -268,6 +269,7 @@ rules:
path-parameters-defined: error
path-params-defined: error
security-defined: error
security-scopes-defined: warn
spec-components-invalid-map-name: error
struct: error
tag-description: warn
Expand Down Expand Up @@ -310,6 +312,7 @@ rules:
path-parameters-defined: error
path-params-defined: error
security-defined: error
security-scopes-defined: warn
spec-components-invalid-map-name: error
struct: error
tag-description: warn
Expand Down Expand Up @@ -343,6 +346,7 @@ rules:
path-parameters-defined: error
path-params-defined: error
security-defined: error
security-scopes-defined: warn
struct: error
tag-description: warn
```
Expand All @@ -358,6 +362,7 @@ rules:
no-required-schema-properties-undefined: warn
no-schema-type-mismatch: error
operation-operationId: warn
security-scopes-defined: warn
struct: error
tag-description: warn
```
Expand All @@ -373,6 +378,7 @@ rules:
no-required-schema-properties-undefined: warn
no-schema-type-mismatch: error
operation-operationId: warn
security-scopes-defined: warn
struct: error
tag-description: warn
```
Expand Down
1 change: 1 addition & 0 deletions docs/@v2/v2.sidebars.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
- page: rules/common/no-schema-type-mismatch.md
- page: rules/common/no-required-schema-properties-undefined.md
- page: rules/common/no-mixed-number-range-constraints.md
- page: rules/common/security-scopes-defined.md
- separator: OpenAPI
- page: rules/oas/array-parameter-serialization.md
- page: rules/oas/boolean-parameter-prefixes.md
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ exports[`resolveConfig > should ignore minimal from the root and read local file
"no-required-schema-properties-undefined": "warn",
"no-schema-type-mismatch": "error",
"operation-operationId": "warn",
"security-scopes-defined": "warn",
"tag-description": "warn",
"tags-alphabetical": "off",
},
Expand All @@ -83,6 +84,7 @@ exports[`resolveConfig > should ignore minimal from the root and read local file
"no-required-schema-properties-undefined": "warn",
"no-schema-type-mismatch": "error",
"operation-operationId": "warn",
"security-scopes-defined": "warn",
"tag-description": "warn",
"tags-alphabetical": "off",
},
Expand Down Expand Up @@ -133,6 +135,7 @@ exports[`resolveConfig > should ignore minimal from the root and read local file
"response-mime-type": "off",
"scalar-property-missing-example": "off",
"security-defined": "error",
"security-scopes-defined": "warn",
"spec-strict-refs": "off",
"tag-description": "warn",
"tags-alphabetical": "off",
Expand Down Expand Up @@ -194,6 +197,7 @@ exports[`resolveConfig > should ignore minimal from the root and read local file
"response-mime-type": "off",
"scalar-property-missing-example": "off",
"security-defined": "error",
"security-scopes-defined": "warn",
"spec-components-invalid-map-name": "error",
"spec-example-values": "off",
"spec-strict-refs": "off",
Expand Down Expand Up @@ -254,6 +258,7 @@ exports[`resolveConfig > should ignore minimal from the root and read local file
"response-mime-type": "off",
"scalar-property-missing-example": "off",
"security-defined": "error",
"security-scopes-defined": "warn",
"spec-components-invalid-map-name": "error",
"spec-example-values": "off",
"spec-strict-refs": "off",
Expand Down Expand Up @@ -313,6 +318,7 @@ exports[`resolveConfig > should ignore minimal from the root and read local file
"response-mime-type": "off",
"scalar-property-missing-example": "off",
"security-defined": "error",
"security-scopes-defined": "warn",
"spec-components-invalid-map-name": "error",
"spec-discriminator-defaultMapping": "warn",
"spec-example-values": "error",
Expand Down Expand Up @@ -484,6 +490,7 @@ exports[`resolveConfig > should resolve extends with local file config which con
"no-required-schema-properties-undefined": "warn",
"no-schema-type-mismatch": "error",
"operation-operationId": "warn",
"security-scopes-defined": "warn",
"tag-description": "warn",
"tags-alphabetical": "off",
},
Expand All @@ -500,6 +507,7 @@ exports[`resolveConfig > should resolve extends with local file config which con
"no-required-schema-properties-undefined": "warn",
"no-schema-type-mismatch": "error",
"operation-operationId": "warn",
"security-scopes-defined": "warn",
"tag-description": "warn",
"tags-alphabetical": "off",
},
Expand Down Expand Up @@ -550,6 +558,7 @@ exports[`resolveConfig > should resolve extends with local file config which con
"response-mime-type": "off",
"scalar-property-missing-example": "off",
"security-defined": "error",
"security-scopes-defined": "warn",
"spec-strict-refs": "off",
"tag-description": "warn",
"tags-alphabetical": "off",
Expand Down Expand Up @@ -611,6 +620,7 @@ exports[`resolveConfig > should resolve extends with local file config which con
"response-mime-type": "off",
"scalar-property-missing-example": "off",
"security-defined": "error",
"security-scopes-defined": "warn",
"spec-components-invalid-map-name": "error",
"spec-example-values": "off",
"spec-strict-refs": "off",
Expand Down Expand Up @@ -671,6 +681,7 @@ exports[`resolveConfig > should resolve extends with local file config which con
"response-mime-type": "off",
"scalar-property-missing-example": "off",
"security-defined": "error",
"security-scopes-defined": "warn",
"spec-components-invalid-map-name": "error",
"spec-example-values": "off",
"spec-strict-refs": "off",
Expand Down Expand Up @@ -730,6 +741,7 @@ exports[`resolveConfig > should resolve extends with local file config which con
"response-mime-type": "off",
"scalar-property-missing-example": "off",
"security-defined": "error",
"security-scopes-defined": "warn",
"spec-components-invalid-map-name": "error",
"spec-discriminator-defaultMapping": "warn",
"spec-example-values": "error",
Expand Down
Loading
Loading