Empire icon indicating copy to clipboard operation
Empire copied to clipboard

[BUG] PydanticInvalidForJsonSchema error when requesting /docs or /redoc endpoints

Open b1tst0rm opened this issue 11 months ago • 1 comments

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

  1. On Kali 2023.3, run setup/install.sh
  2. Activate the virtualenv and run the server: source .venv/bin/activate && ./ps-empire server
  3. 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]

b1tst0rm avatar Apr 05 '24 20:04 b1tst0rm

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"

vinnybod avatar Apr 06 '24 19:04 vinnybod