fides icon indicating copy to clipboard operation
fides copied to clipboard

Special Characters in Custom Taxonomy Entities Throw `500` Errors

Open RobertKeyser opened this issue 3 years ago • 2 comments

Bug Description

When adding a Taxonomy Entity, if a user includes a special character or whitespace, the API returns a 500 - internal server error and presents that in the UI.

image

Steps to Reproduce

  1. Spin up a server
  2. Browse to http://localhost:3000/taxonomy
  3. Click Add Taxonomy Entity +
  4. In the Data Category field, enter a name that either has a special character (e.g. something_10%) or a whitespace (e.g. this is a test)
  5. Receive Internal Server Error
  6. Check logs on server
Expand for full stacktrace
2023-01-06 22:45:56.900 [ERROR] (logger:_log_exception:24): FidesKeys must only contain alphanumeric characters, '.', '_' or '-'. Value provided: this is a test
Traceback (most recent call last):

  File "/root/.local/lib/python3.10/site-packages/anyio/streams/memory.py", line 94, in receive
    return self.receive_nowait()
           │    └ <function MemoryObjectReceiveStream.receive_nowait at 0xffffb116d990>
           └ MemoryObjectReceiveStream(_state=MemoryObjectStreamState(max_buffer_size=0, buffer=deque([]), open_send_channels=0, open_rece...
  File "/root/.local/lib/python3.10/site-packages/anyio/streams/memory.py", line 89, in receive_nowait
    raise WouldBlock
          └ <class 'anyio.WouldBlock'>

anyio.WouldBlock


During handling of the above exception, another exception occurred:


Traceback (most recent call last):

  File "/root/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 43, in call_next
    message = await recv_stream.receive()
                    │           └ <function MemoryObjectReceiveStream.receive at 0xffffb116da20>
                    └ MemoryObjectReceiveStream(_state=MemoryObjectStreamState(max_buffer_size=0, buffer=deque([]), open_send_channels=0, open_rece...
  File "/root/.local/lib/python3.10/site-packages/anyio/streams/memory.py", line 114, in receive
    raise EndOfStream
          └ <class 'anyio.EndOfStream'>

anyio.EndOfStream


During handling of the above exception, another exception occurred:


Traceback (most recent call last):

  File "<string>", line 1, in <module>
  File "/usr/local/lib/python3.10/multiprocessing/spawn.py", line 116, in spawn_main
    exitcode = _main(fd, parent_sentinel)
               │     │   └ 4
               │     └ 7
               └ <function _main at 0xffffb1d0ec20>
  File "/usr/local/lib/python3.10/multiprocessing/spawn.py", line 129, in _main
    return self._bootstrap(parent_sentinel)
           │    │          └ 4
           │    └ <function BaseProcess._bootstrap at 0xffffb1f35cf0>
           └ <SpawnProcess name='SpawnProcess-1' parent=1 started>
  File "/usr/local/lib/python3.10/multiprocessing/process.py", line 314, in _bootstrap
    self.run()
    │    └ <function BaseProcess.run at 0xffffb1f35360>
    └ <SpawnProcess name='SpawnProcess-1' parent=1 started>
  File "/usr/local/lib/python3.10/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
    │    │        │    │        │    └ {'config': <uvicorn.config.Config object at 0xffffb1d0ae90>, 'target': <bound method Server.run of <uvicorn.server.Server obj...
    │    │        │    │        └ <SpawnProcess name='SpawnProcess-1' parent=1 started>
    │    │        │    └ ()
    │    │        └ <SpawnProcess name='SpawnProcess-1' parent=1 started>
    │    └ <function subprocess_started at 0xffffb12c3880>
    └ <SpawnProcess name='SpawnProcess-1' parent=1 started>
  File "/root/.local/lib/python3.10/site-packages/uvicorn/subprocess.py", line 76, in subprocess_started
    target(sockets=sockets)
    │              └ [<socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 8080)>]
    └ <bound method Server.run of <uvicorn.server.Server object at 0xffffb1d0aef0>>
  File "/root/.local/lib/python3.10/site-packages/uvicorn/server.py", line 60, in run
    return asyncio.run(self.serve(sockets=sockets))
           │       │   │    │             └ [<socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 8080)>]
           │       │   │    └ <function Server.serve at 0xffffb12c2b90>
           │       │   └ <uvicorn.server.Server object at 0xffffb1d0aef0>
           │       └ <function run at 0xffffb1d15d80>
           └ <module 'asyncio' from '/usr/local/lib/python3.10/asyncio/__init__.py'>
  File "/usr/local/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
           │    │                  └ <coroutine object Server.serve at 0xffffb10d7bc0>
           │    └ <method 'run_until_complete' of 'uvloop.loop.Loop' objects>
           └ <uvloop.Loop running=True closed=False debug=False>
  File "/root/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 36, in coro
    await self.app(scope, request.receive, send_stream.send)
          │    │   │      │       │        │           └ <function MemoryObjectSendStream.send at 0xffffb116e170>
          │    │   │      │       │        └ MemoryObjectSendStream(_state=MemoryObjectStreamState(max_buffer_size=0, buffer=deque([]), open_send_channels=1, open_receive...
          │    │   │      │       └ <property object at 0xffffaf9c9170>
          │    │   │      └ <starlette.requests.Request object at 0xffff843365f0>
          │    │   └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.3'}, 'http_version': '1.1', 'server': ('172.30.0.8', 8080), 'c...
          │    └ <starlette.middleware.cors.CORSMiddleware object at 0xffff8ccc82b0>
          └ <starlette.middleware.base.BaseHTTPMiddleware object at 0xffff8ccc82e0>
  File "/root/.local/lib/python3.10/site-packages/starlette/middleware/cors.py", line 92, in __call__
    await self.simple_response(scope, receive, send, request_headers=headers)
          │    │               │      │        │                     └ Headers({'x-forwarded-host': 'localhost:3000', 'x-forwarded-proto': 'http', 'x-forwarded-port': '3000', 'x-forwarded-for': ':...
          │    │               │      │        └ <bound method MemoryObjectSendStream.send of MemoryObjectSendStream(_state=MemoryObjectStreamState(max_buffer_size=0, buffer=...
          │    │               │      └ <bound method RequestResponseCycle.receive of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0xffff843...
          │    │               └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.3'}, 'http_version': '1.1', 'server': ('172.30.0.8', 8080), 'c...
          │    └ <function CORSMiddleware.simple_response at 0xffffaec8ac20>
          └ <starlette.middleware.cors.CORSMiddleware object at 0xffff8ccc82b0>
  File "/root/.local/lib/python3.10/site-packages/starlette/middleware/cors.py", line 147, in simple_response
    await self.app(scope, receive, send)
          │    │   │      │        └ functools.partial(<bound method CORSMiddleware.send of <starlette.middleware.cors.CORSMiddleware object at 0xffff8ccc82b0>>, ...
          │    │   │      └ <bound method RequestResponseCycle.receive of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0xffff843...
          │    │   └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.3'}, 'http_version': '1.1', 'server': ('172.30.0.8', 8080), 'c...
          │    └ <starlette.middleware.base.BaseHTTPMiddleware object at 0xffff8ccc8250>
          └ <starlette.middleware.cors.CORSMiddleware object at 0xffff8ccc82b0>
  File "/root/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 68, in __call__
    response = await self.dispatch_func(request, call_next)
                     │    │             │        └ <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0xffff8cbad630>
                     │    │             └ <starlette.requests.Request object at 0xffff76e21570>
                     │    └ <function dispatch_log_request at 0xffff8e9b6560>
                     └ <starlette.middleware.base.BaseHTTPMiddleware object at 0xffff8ccc8250>

> File "/fides/src/fides/api/main.py", line 101, in dispatch_log_request
    response = await call_next(request)
                     │         └ <starlette.requests.Request object at 0xffff76e21570>
                     └ <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0xffff8cbad630>

  File "/root/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 46, in call_next
    raise app_exc
          └ FidesValidationError("FidesKeys must only contain alphanumeric characters, '.', '_' or '-'. Value provided: this is a test")
  File "/root/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 36, in coro
    await self.app(scope, request.receive, send_stream.send)
          │    │   │      │       │        │           └ <function MemoryObjectSendStream.send at 0xffffb116e170>
          │    │   │      │       │        └ MemoryObjectSendStream(_state=MemoryObjectStreamState(max_buffer_size=0, buffer=deque([]), open_send_channels=0, open_receive...
          │    │   │      │       └ <property object at 0xffffaf9c9170>
          │    │   │      └ <starlette.requests.Request object at 0xffff76e21570>
          │    │   └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.3'}, 'http_version': '1.1', 'server': ('172.30.0.8', 8080), 'c...
          │    └ <slowapi.middleware.SlowAPIMiddleware object at 0xffff8ccabf70>
          └ <starlette.middleware.base.BaseHTTPMiddleware object at 0xffff8ccc8250>
  File "/root/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 68, in __call__
    response = await self.dispatch_func(request, call_next)
                     │    │             │        └ <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0xffff8cbac280>
                     │    │             └ <starlette.requests.Request object at 0xffff76410be0>
                     │    └ <bound method SlowAPIMiddleware.dispatch of <slowapi.middleware.SlowAPIMiddleware object at 0xffff8ccabf70>>
                     └ <slowapi.middleware.SlowAPIMiddleware object at 0xffff8ccabf70>
  File "/root/.local/lib/python3.10/site-packages/slowapi/middleware.py", line 56, in dispatch
    response = await call_next(request)
                     │         └ <starlette.requests.Request object at 0xffff76410be0>
                     └ <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0xffff8cbac280>
  File "/root/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 46, in call_next
    raise app_exc
          └ FidesValidationError("FidesKeys must only contain alphanumeric characters, '.', '_' or '-'. Value provided: this is a test")
  File "/root/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 36, in coro
    await self.app(scope, request.receive, send_stream.send)
          │    │   │      │       │        │           └ <function MemoryObjectSendStream.send at 0xffffb116e170>
          │    │   │      │       │        └ MemoryObjectSendStream(_state=MemoryObjectStreamState(max_buffer_size=0, buffer=deque([]), open_send_channels=0, open_receive...
          │    │   │      │       └ <property object at 0xffffaf9c9170>
          │    │   │      └ <starlette.requests.Request object at 0xffff76410be0>
          │    │   └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.3'}, 'http_version': '1.1', 'server': ('172.30.0.8', 8080), 'c...
          │    └ <starlette.exceptions.ExceptionMiddleware object at 0xffff8ccabf10>
          └ <slowapi.middleware.SlowAPIMiddleware object at 0xffff8ccabf70>
  File "/root/.local/lib/python3.10/site-packages/starlette/exceptions.py", line 93, in __call__
    raise exc
  File "/root/.local/lib/python3.10/site-packages/starlette/exceptions.py", line 82, in __call__
    await self.app(scope, receive, sender)
          │    │   │      │        └ <function ExceptionMiddleware.__call__.<locals>.sender at 0xffff765265f0>
          │    │   │      └ <bound method RequestResponseCycle.receive of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0xffff843...
          │    │   └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.3'}, 'http_version': '1.1', 'server': ('172.30.0.8', 8080), 'c...
          │    └ <fastapi.middleware.asyncexitstack.AsyncExitStackMiddleware object at 0xffff8ccabee0>
          └ <starlette.exceptions.ExceptionMiddleware object at 0xffff8ccabf10>
  File "/root/.local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__
    raise e
  File "/root/.local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
    await self.app(scope, receive, send)
          │    │   │      │        └ <function ExceptionMiddleware.__call__.<locals>.sender at 0xffff765265f0>
          │    │   │      └ <bound method RequestResponseCycle.receive of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0xffff843...
          │    │   └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.3'}, 'http_version': '1.1', 'server': ('172.30.0.8', 8080), 'c...
          │    └ <fastapi.routing.APIRouter object at 0xffffaf5dae30>
          └ <fastapi.middleware.asyncexitstack.AsyncExitStackMiddleware object at 0xffff8ccabee0>
  File "/root/.local/lib/python3.10/site-packages/starlette/routing.py", line 670, in __call__
    await route.handle(scope, receive, send)
          │     │      │      │        └ <function ExceptionMiddleware.__call__.<locals>.sender at 0xffff765265f0>
          │     │      │      └ <bound method RequestResponseCycle.receive of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0xffff843...
          │     │      └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.3'}, 'http_version': '1.1', 'server': ('172.30.0.8', 8080), 'c...
          │     └ <function Route.handle at 0xffffaf9df5b0>
          └ <fastapi.routing.APIRoute object at 0xffff8e9ca500>
  File "/root/.local/lib/python3.10/site-packages/starlette/routing.py", line 266, in handle
    await self.app(scope, receive, send)
          │    │   │      │        └ <function ExceptionMiddleware.__call__.<locals>.sender at 0xffff765265f0>
          │    │   │      └ <bound method RequestResponseCycle.receive of <uvicorn.protocols.http.httptools_impl.RequestResponseCycle object at 0xffff843...
          │    │   └ {'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.3'}, 'http_version': '1.1', 'server': ('172.30.0.8', 8080), 'c...
          │    └ <function request_response.<locals>.app at 0xffff8e9b6a70>
          └ <fastapi.routing.APIRoute object at 0xffff8e9ca500>
  File "/root/.local/lib/python3.10/site-packages/starlette/routing.py", line 65, in app
    response = await func(request)
                     │    └ <starlette.requests.Request object at 0xffff84203a00>
                     └ <function get_request_handler.<locals>.app at 0xffff8e9b68c0>
  File "/root/.local/lib/python3.10/site-packages/fastapi/routing.py", line 221, in app
    solved_result = await solve_dependencies(
                          └ <function solve_dependencies at 0xffffaf59e560>
  File "/root/.local/lib/python3.10/site-packages/fastapi/dependencies/utils.py", line 560, in solve_dependencies
    ) = await request_body_to_args(  # body_params checked above
              └ <function request_body_to_args at 0xffffaf59e680>
  File "/root/.local/lib/python3.10/site-packages/fastapi/dependencies/utils.py", line 695, in request_body_to_args
    v_, errors_ = field.validate(value, values, loc=loc)
                  │     │        │      │           └ ('body',)
                  │     │        │      └ {}
                  │     │        └ {'fides_key': 'this is a test', 'name': '', 'description': '', 'is_default': False}
                  │     └ <function ModelField.validate at 0xffffb0b48280>
                  └ ModelField(name='resource', type=DataCategory, required=True)
  File "/root/.local/lib/python3.10/site-packages/pydantic/fields.py", line 857, in validate
    v, errors = self._validate_singleton(v, values, loc, cls)
    │           │    │                   │  │       │    └ None
    │           │    │                   │  │       └ ('body',)
    │           │    │                   │  └ {}
    │           │    │                   └ {'fides_key': 'this is a test', 'name': '', 'description': '', 'is_default': False}
    │           │    └ <function ModelField._validate_singleton at 0xffffb0b485e0>
    │           └ ModelField(name='resource', type=DataCategory, required=True)
    └ {'fides_key': 'this is a test', 'name': '', 'description': '', 'is_default': False}
  File "/root/.local/lib/python3.10/site-packages/pydantic/fields.py", line 1074, in _validate_singleton
    return self._apply_validators(v, values, loc, cls, self.validators)
           │    │                 │  │       │    │    │    └ <member 'validators' of 'ModelField' objects>
           │    │                 │  │       │    │    └ ModelField(name='resource', type=DataCategory, required=True)
           │    │                 │  │       │    └ None
           │    │                 │  │       └ ('body',)
           │    │                 │  └ {}
           │    │                 └ {'fides_key': 'this is a test', 'name': '', 'description': '', 'is_default': False}
           │    └ <function ModelField._apply_validators at 0xffffb0b48700>
           └ ModelField(name='resource', type=DataCategory, required=True)
  File "/root/.local/lib/python3.10/site-packages/pydantic/fields.py", line 1121, in _apply_validators
    v = validator(cls, v, values, self, self.model_config)
        │         │    │  │       │     │    └ <member 'model_config' of 'ModelField' objects>
        │         │    │  │       │     └ ModelField(name='resource', type=DataCategory, required=True)
        │         │    │  │       └ ModelField(name='resource', type=DataCategory, required=True)
        │         │    │  └ {}
        │         │    └ {'fides_key': 'this is a test', 'name': '', 'description': '', 'is_default': False}
        │         └ None
        └ <function BaseModel.validate at 0xffff8e9b6950>
  File "/root/.local/lib/python3.10/site-packages/pydantic/class_validators.py", line 313, in <lambda>
    return lambda cls, v, values, field, config: validator(v)
                  │    │  │       │      │       │         └ {'fides_key': 'this is a test', 'name': '', 'description': '', 'is_default': False}
                  │    │  │       │      │       └ <bound method BaseModel.validate of <class 'fideslang.models.DataCategory'>>
                  │    │  │       │      └ <class 'pydantic.config.BaseConfig'>
                  │    │  │       └ ModelField(name='resource', type=DataCategory, required=True)
                  │    │  └ {}
                  │    └ {'fides_key': 'this is a test', 'name': '', 'description': '', 'is_default': False}
                  └ None
  File "/root/.local/lib/python3.10/site-packages/pydantic/main.py", line 704, in validate
    return cls(**value)
           │     └ {'fides_key': 'this is a test', 'name': '', 'description': '', 'is_default': False}
           └ <class 'fideslang.models.DataCategory'>
  File "/root/.local/lib/python3.10/site-packages/pydantic/main.py", line 339, in __init__
    values, fields_set, validation_error = validate_model(__pydantic_self__.__class__, data)
                                           │              │                 │          └ {'fides_key': 'this is a test', 'name': '', 'description': '', 'is_default': False}
                                           │              │                 └ <attribute '__class__' of 'object' objects>
                                           │              └ DataCategory()
                                           └ <function validate_model at 0xffffb0b4ba30>
  File "/root/.local/lib/python3.10/site-packages/pydantic/main.py", line 1056, in validate_model
    v_, errors_ = field.validate(value, values, loc=field.alias, cls=cls_)
                  │     │        │      │           │     │          └ <class 'fideslang.models.DataCategory'>
                  │     │        │      │           │     └ <member 'alias' of 'ModelField' objects>
                  │     │        │      │           └ ModelField(name='fides_key', type=FidesKey, required=True)
                  │     │        │      └ {}
                  │     │        └ 'this is a test'
                  │     └ <function ModelField.validate at 0xffffb0b48280>
                  └ ModelField(name='fides_key', type=FidesKey, required=True)
  File "/root/.local/lib/python3.10/site-packages/pydantic/fields.py", line 857, in validate
    v, errors = self._validate_singleton(v, values, loc, cls)
    │           │    │                   │  │       │    └ <class 'fideslang.models.DataCategory'>
    │           │    │                   │  │       └ 'fides_key'
    │           │    │                   │  └ {}
    │           │    │                   └ 'this is a test'
    │           │    └ <function ModelField._validate_singleton at 0xffffb0b485e0>
    │           └ ModelField(name='fides_key', type=FidesKey, required=True)
    └ 'this is a test'
  File "/root/.local/lib/python3.10/site-packages/pydantic/fields.py", line 1074, in _validate_singleton
    return self._apply_validators(v, values, loc, cls, self.validators)
           │    │                 │  │       │    │    │    └ <member 'validators' of 'ModelField' objects>
           │    │                 │  │       │    │    └ ModelField(name='fides_key', type=FidesKey, required=True)
           │    │                 │  │       │    └ <class 'fideslang.models.DataCategory'>
           │    │                 │  │       └ 'fides_key'
           │    │                 │  └ {}
           │    │                 └ 'this is a test'
           │    └ <function ModelField._apply_validators at 0xffffb0b48700>
           └ ModelField(name='fides_key', type=FidesKey, required=True)
  File "/root/.local/lib/python3.10/site-packages/pydantic/fields.py", line 1121, in _apply_validators
    v = validator(cls, v, values, self, self.model_config)
        │         │    │  │       │     │    └ <member 'model_config' of 'ModelField' objects>
        │         │    │  │       │     └ ModelField(name='fides_key', type=FidesKey, required=True)
        │         │    │  │       └ ModelField(name='fides_key', type=FidesKey, required=True)
        │         │    │  └ {}
        │         │    └ 'this is a test'
        │         └ <class 'fideslang.models.DataCategory'>
        └ <function FidesKey.validate at 0xffffafc0e680>
  File "/root/.local/lib/python3.10/site-packages/pydantic/class_validators.py", line 313, in <lambda>
    return lambda cls, v, values, field, config: validator(v)
                  │    │  │       │      │       │         └ 'this is a test'
                  │    │  │       │      │       └ <bound method FidesKey.validate of <class 'fideslang.validation.FidesKey'>>
                  │    │  │       │      └ <class 'pydantic.config.Config'>
                  │    │  │       └ ModelField(name='fides_key', type=FidesKey, required=True)
                  │    │  └ {}
                  │    └ 'this is a test'
                  └ <class 'fideslang.models.DataCategory'>
  File "/root/.local/lib/python3.10/site-packages/fideslang/validation.py", line 30, in validate
    raise FidesValidationError(
          └ <class 'fideslang.validation.FidesValidationError'>

fideslang.validation.FidesValidationError: FidesKeys must only contain alphanumeric characters, '.', '_' or '-'. Value provided: this is a test

Expected behavior

A few things:

  • Return 400 including reason for error
  • Validate the field on the client side to prevent it from happening at all

Environment

  • Version: 2.4.0
  • OS: MacOS
  • Python Version: 3.10.4
  • Docker Version:

RobertKeyser avatar Jan 06 '23 23:01 RobertKeyser

@Roger-Ethyca to double check

Roger-Ethyca avatar Mar 21 '23 18:03 Roger-Ethyca

@Roger-Ethyca still an issue?

rsilvery avatar Oct 20 '23 15:10 rsilvery