stoplightio / spectral

A flexible JSON/YAML linter for creating automated style guides, with baked in support for OpenAPI (v3.1, v3.0, and v2.0), Arazzo v1.0, as well as AsyncAPI v2.x.
https://stoplight.io/spectral
Apache License 2.0
2.53k stars 240 forks source link

Validation using the `schema` does not detect schema errors if the target is a `$ref` reference #2245

Closed c-pius closed 2 years ago

c-pius commented 2 years ago

Describe the bug Validation using the schema function does not detect schema errors if the target is a $ref reference.

To Reproduce

  1. Given the following AsyncAPI document:

note that the message of the light/measured operation is reference from components/schemas

asyncapi: "2.4.0"
info:
  title: Streetlights API
  version: "1.0.0"
  description: |
    The Smartylighting Streetlights API allows you
    to remotely manage the city lights.
channels:
  light/measured:
    publish:
      summary: Inform about environmental lighting conditions for a particular streetlight.
      operationId: onLightMeasured
      message:
        $ref: "#/components/schemas/LightMeasured"
components:
  messages: {}
  schemas:
    LightMeasured:
      name: LightMeasured
      payload:
        type: object
        properties:
          id:
            type: integer
            minimum: 0
            description: Id of the streetlight.
  1. Run the following rule

note that it enforces a) that the channel/operation message is referenced and not defined directly in the channel, b) that the location of the reference's re-use object is #/components/messages

"message-defined-in-components-messages": {
      message: "Message defined in '$.components.messages'.",
      resolved: false,
      severity: DiagnosticSeverity.Error,
      given: "$.channels.*.subscribe.message",
      then: {
        function: schema,
        functionOptions: {
          schema: {
            type: "object",
            required: ["$ref"],
            properties: {
              $ref: {
                type: "string",
                pattern: "^#\\/components\\/messages\\/",
              },
            },
            additionalProperties: false,
          },
        },
      },
    }
  1. See that no validation error is returned, as opposed to returning an error since the content of $ref: "#/components/schemas/LightMeasured" does not match the pattern ^#\\/components\\/messages\\/.

Alternatively, add the following test to schema.test.ts and note that it fails (result = []).

  it('validates $ref path', async() => {
    const schema = {
      type: "object",
      required: ["$ref"],
      properties: {
        $ref: {
          type: "string",
          pattern: "^#\\/components\\/messages\\/",
        },
      },
      additionalProperties: false,
    };

    const result = await runSchema({"$ref": "#/components/schemas/LightMeasured"}, {schema})
    expect(result).toHaveLength(1);
  });

Expected behavior

Validation should return the error that the content of the $ref does not match the expected pattern.

Screenshots The validation using ajv does detect the schema error as expected:

image

The problem seems to be within betterAjvErrors. Concretely, in the makeTree function the path is null as the JSON_POINTERS_REGEX does not like the JSONPointer including the $ char. It may be a simple fix by adding $ to the JSON_POINTERS_REGEX, but I cannot judge what side effects this would have.

image

Environment (remove any that are not applicable):

stoplight-bot commented 2 years ago

:tada: This issue has been resolved in version @stoplight/spectral-functions-v1.7.1 :tada:

The release is available on npm package (@latest dist-tag)

Your semantic-release bot :package::rocket:

P0lip commented 2 years ago

Thanks a bunch @c-pius!