Empire
Empire copied to clipboard
[BUG] PydanticInvalidForJsonSchema error when requesting /docs or /redoc endpoints
Is there an existing issue for this?
- [X] I have searched the existing issues
Empire Version
5.9.5
Python Version
3.11.8
Operating System
Kali Rolling 2023.3
Database
MariaDB
Current Behavior
When requesting /docs (or /redoc), those endpoints 200 however the request for openapi.json is a 500. The following error output is generated by the server:
[INFO]: 10.255.255.253:37778 - "GET /redoc?vscodeBrowserReqId=1712347444857 HTTP/1.1" 200
[INFO]: 10.255.255.253:37778 - "GET /openapi.json HTTP/1.1" 500
[ERROR]: Exception in ASGI application
Traceback (most recent call last):
...
File "/home/<redacted>/empire/.venv/lib/python3.11/site-packages/pydantic/json_schema.py", line 2074, in handle_invalid_for_json_schema
raise PydanticInvalidForJsonSchema(f'Cannot generate a JsonSchema for {error_info}')
pydantic.errors.PydanticInvalidForJsonSchema: Cannot generate a JsonSchema for core_schema.PlainValidatorFunctionSchema ({'type': 'no-info', 'function': <bound method CustomOptionSchema.check_value of <class 'empire.server.api.v2.shared_dto.CustomOptionSchema'>>})
For further information visit https://errors.pydantic.dev/2.5/u/invalid-for-json-schema
Expected Behavior
When requesting /docs, FastAPI Swagger UI loads without error showing API endpoints.
Steps To Reproduce
- On Kali 2023.3, run
setup/install.sh
- Activate the virtualenv and run the server:
source .venv/bin/activate && ./ps-empire server
- Visit in a browser: http://localhost:1337/docs
Anything else?
It appears the issue lies within the empire.server.api.v2.shared_dto.CustomOptionSchema
class, particularly the methods check_value
and check_suggested_values
trigger the PydanticInvalidForJsonSchema error.
https://github.com/BC-SECURITY/Empire/blob/main/empire/server/api/v2/shared_dto.py#L33
The 500 can be "fixed" by commenting the following lines of empire/server/api/v2/shared_dto.py
and then you can access the /docs and /redoc endpoints without issues:
Lines 35-43
class CustomOptionSchema(BaseModel):
description: str
required: bool
value: str
suggested_values: list[str]
strict: bool
value_type: ValueType
# Ensure the functionality of pydantic v1 coercing values to strings
# https://github.com/pydantic/pydantic/issues/5606
# @field_validator("value", mode="plain")
# @classmethod
# def check_value(cls, v):
# return str(v)
#
# @field_validator("suggested_values", mode="plain")
# @classmethod
# def check_suggested_values(cls, v):
# return [str(value) for value in v]
Thanks, I have a fix for this that will be in 5.10 in a couple weeks. until then, this is the patch that can be applied (git apply <patch>
)
diff --git a/empire/server/api/v2/shared_dto.py b/empire/server/api/v2/shared_dto.py
index 353ad15f..7664c0f4 100644
--- a/empire/server/api/v2/shared_dto.py
+++ b/empire/server/api/v2/shared_dto.py
@@ -1,7 +1,11 @@
from enum import Enum
from typing import Annotated, Any
-from pydantic import BaseModel, BeforeValidator, ConfigDict, field_validator
+from pydantic import (
+ BaseModel,
+ BeforeValidator,
+ ConfigDict,
+)
from empire.server.core.db import models
@@ -22,26 +26,22 @@ class ValueType(str, Enum):
file = "FILE"
+# Ensure the functionality of pydantic v1 coercing values to strings
+# https://github.com/pydantic/pydantic/issues/5606
+def coerce_to_string(v: Any):
+ if isinstance(v, list):
+ return [str(value) for value in v]
+ return str(v)
+
+
class CustomOptionSchema(BaseModel):
description: str
required: bool
- value: str
- suggested_values: list[str]
+ value: Annotated[str, BeforeValidator(coerce_to_string)]
+ suggested_values: Annotated[list[str], BeforeValidator(coerce_to_string)]
strict: bool
value_type: ValueType
- # Ensure the functionality of pydantic v1 coercing values to strings
- # https://github.com/pydantic/pydantic/issues/5606
- @field_validator("value", mode="plain")
- @classmethod
- def check_value(cls, v):
- return str(v)
-
- @field_validator("suggested_values", mode="plain")
- @classmethod
- def check_suggested_values(cls, v):
- return [str(value) for value in v]
-
class OrderDirection(str, Enum):
asc = "asc"