Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature]: Required Access to resource's OpenAPI attributes whose validation is failing #609

Closed
rohan-97 opened this issue Jun 23, 2023 · 3 comments

Comments

@rohan-97
Copy link

rohan-97 commented Jun 23, 2023

Suggested Behavior

Hello, I am currently using OpenAPI-core with my Flask app and I am customizing validation error responses as per following comment.

The issue I am facing is that there are some fields in request which holds sensitive information, e.g credentials, Auth-tokens.
When the validation fails on these fields we get an error string which includes value of these sensitive fields.

E.g consider following response,

{
    "errors": [
        {
            "class": "<class 'openapi_core.validation.schemas.exceptions.InvalidSchemaValue'>",
            "status": 400,
            "title": "Value {'flag': 'MyPassword1234'} not valid for schema of type object: (<ValidationError: \"'MyPassword1234' is too short\">,)"
        }
    ]
}

As we can see in above example, the password of user is exposed as part of response.
I do want to add validation for these fields however I don't want the values of these fields to be send as a response.

For that I checked whether there is a flag in OpenAPI which marks a field as sensitive and found this issue which suggested usingx-pii: true field in the yaml

Also I used FlaskOpenAPIErrorsHandler to fetch the error object and see if we get details of the flags which are set for the field where the validation failed.

Following is my Flask code

#!/usr/bin/python3
"""Test server."""

from flask import Flask, request, jsonify
from openapi_core.contrib.flask.decorators import FlaskOpenAPIViewDecorator, FlaskOpenAPIErrorsHandler
from openapi_core import Spec

# Custom Error Handler block
class ErrorHandler(FlaskOpenAPIErrorsHandler):
    """"Custom Error Handler"""
    def handle(self, errors:list):
        return jsonify({
            "causedBy" : [self.handle_error(error) for error in errors]
        }), self.OPENAPI_ERROR_STATUS.get(errors[0].__class__, 400)

    def handle_error(self, error):
        """
        Converts error object into error string message

        :param error: Error object which stores exception message
        :type error: Exception object
        :return: Error message string corresponding to error object
        :rtype: str
        """
        if error.__cause__ is not None:
            error = error.__cause__
        # TODO: If the field in error object has x-pii: true, return a generic string which does not include it's value
        if not (hasattr(error, "value") and hasattr(error, "type") and hasattr(error, "schema_errors")):
            return str(error)
        return f"Value(s) {error.value} not valid for schema of type {error.type} errors: {', '.join([err.message for err in error.schema_errors])}"


SPEC = "test.yaml"
obj = ErrorHandler()
openapi = FlaskOpenAPIViewDecorator.from_spec(Spec.from_file_path(SPEC), openapi_errors_handler=obj)

app = Flask(__name__)

@app.route("/test", methods=["POST"])
@openapi
def read_permission():
    """Test function"""
    return jsonify({
                    "flag stri_normal_json": request.json.get("flag", 1)
                })

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=345, debug=True)

And following is the Yaml file

openapi: '3.0.2'
info:
  title: Test Title
  version: '1.0'
servers:
  - url: http://localhost:345/
paths:
  /test:
    post:
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - flag
              properties:
                flag:
                  x-pii: true
                  type: string
                  pattern: "^[\\w.-]*$"
                  minLength: 6
                  maxLength: 20
      responses:
        200:
          description: Sample response
          content:
            application/json:
              schema:
                type: object
                properties:
                  flag stri_json:
                    type: string
                    minLength: 6
                    maxLength: 20

Please check the TODO comment inside handle_error method.

If the validation of field flag fails and if we have access to attributes of flag yaml properties e.g below properties

                flag:
                  x-pii: true
                  type: string
                  pattern: "^[\\w.-]*$"
                  minLength: 6
                  maxLength: 20

Then we would be able to have better customization and control over the response message.

Why is this needed?

Many times we need to have better control over the response generated on the failure of validation.
Current error messages generated by openapi-core exposes the field contents as part of response which makes openapi-core useless if there are sensitive fields in request body which needs to be validated.1

Having access to OpenAPI attributes of any field would be very helpful in generating custom response messages and would help us to perform validation on fields with sensitive data and also not expose the sensitive information to the response.

References

OAI/OpenAPI-Specification#2190

Would you like to implement a feature?

Yes

@p1c2u
Copy link
Collaborator

p1c2u commented Jun 23, 2023

Hi @rohan-97

There's not need for extension; it's a use case for a password format defined in OpenAPI 3.x

I would see this as a format unmarshaller which returns extended string type (similar to SecretStr in pydantic) and converting to string by default will give us masked string.

As a workaround for now I would catch/override InvalidSchemaValueexception or define custom unmarshaler for password format.

@p1c2u
Copy link
Collaborator

p1c2u commented Jun 23, 2023

My second thought I noticed you want to mask invalid value of a property. That means format unmarshaller won't event trigger.

@rohan-97
Copy link
Author

Hello @p1c2u ,
I think the control won't hit format unmarshaller.

However I was able to get the resource's openapi attributes by checking attributes of error.schema_errors objects.
schema_errors objects had following attributes

schema: provides detail about OpenAPI attributes
validator: Provides the attributesname on which the validation failed e.g minlength, pattern
absolute_path: path of field name in entire OpenAPI schema

Using above attribute error.schema_errors[0].schema, I am able to query attributes of the validation which is failing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants