Repository: hyperledger/aries-cloudagent-python

Issue:
Corrupted input (present-proof request) breaks the output API (present proof records)
We just noticed while testing out the Askar wallet type with BBS+ activated, that corrupted input on the `/present-proof-2.0/send-request` endpoint can break the `/present-proof-2.0/records` API endpoint.

`/present-proof-2.0/send-request'
Request Payload:
```
{
  "comment": "string",
  "connection_id": "3b5b30f4-6289-4814-88ae-ac8dd1f03fc5",
  "presentation_request": {
    "dif": {
      **"_schema": [{
        "uri": "https://www.w3.org/2018/credentials/#VerifiableCredential"
      }],**
      "presentation_definition": {
        "format": {
          "ldp_vp": {
            "proof_type": "BbsBlsSignature2020"
          }
        },
        "id": "fa2c4a76-c7bd-4313-a0f8-d9f5979c1fd2",
        "input_descriptors": [{
          "schema": [{
              "uri": "https://www.w3.org/2018/credentials/#VerifiableCredential"
            }
          ],
          "constraints": {
            "fields": [{
                "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                "path": ["$.credentialSubject.id"]
              }
            ],
            "is_holder": [{
              "directive": "required",
              "field_id": [
                "3fa85f64-5717-4562-b3fc-2c963f66afa6"
              ]
            }],
            "limit_disclosure": "required"
          },
          "id": "XXXXXXX",
          "name": "XXXXXXX"
        }]
      }
    }
  }
}
```

Don't ask me how the `_schema` ended up in there, that's a fail on our side. But if you send this request and then fetch the records:

`/present-proof-2.0/records`:
Return code: `400 Error: Bad Request`
Return body:
```
400: V20PresRequest schema validation failed. {'_schema`: ['Unknown field.']}.
```

It looks like there is some validation happening that should normally be applied upon user input when fetching data from the persistence. This locks up the endpoint for the whole instance.

Image used: `bcgovimages/aries-cloudagent:py36-1.16-1_0.7.2-rc0`


Discussion:

Resolving patch:
diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_request_schema.py b/aries_cloudagent/protocols/present_proof/dif/pres_request_schema.py
--- a/aries_cloudagent/protocols/present_proof/dif/pres_request_schema.py
+++ b/aries_cloudagent/protocols/present_proof/dif/pres_request_schema.py
@@ -1,13 +1,44 @@
 """DIF Proof Request Schema."""
-from marshmallow import fields
+from marshmallow import fields, INCLUDE
+from typing import Optional, Union
 
+from ....messaging.models.base import BaseModel, BaseModelSchema
 from ....messaging.models.openapi import OpenAPISchema
 
-from .pres_exch import PresentationDefinitionSchema, DIFOptionsSchema
+from .pres_exch import (
+    PresentationDefinitionSchema,
+    PresentationDefinition,
+    DIFOptionsSchema,
+    DIFOptions,
+)
 
 
-class DIFProofRequestSchema(OpenAPISchema):
-    """Schema for DIF Proof request."""
+class DIFProofRequest(BaseModel):
+    """DIF presentation request input detail."""
+
+    class Meta:
+        """DIFProofRequest metadata."""
+
+        schema_class = "DIFProofRequestSchema"
+
+    def __init__(
+        self,
+        presentation_definition: Optional[Union[dict, PresentationDefinition]],
+        options: Optional[Union[dict, DIFOptions]] = None,
+    ) -> None:
+        """Initialize the DIFProofRequest instance."""
+        self.presentation_definition = presentation_definition
+        self.options = options
+
+
+class DIFProofRequestSchema(BaseModelSchema):
+    """Schema for DIF presentation request."""
+
+    class Meta:
+        """Accept parameter overload."""
+
+        unknown = INCLUDE
+        model_class = DIFProofRequest
 
     options = fields.Nested(
         DIFOptionsSchema(),


Test patch:
diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_request.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_request.py
--- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_request.py
+++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_request.py
@@ -94,15 +94,15 @@ def test_limit_disclosure(self):
 
         pres_request_a = DIFProofRequestSchema().load(test_pd_a)
         test_limit_disclosure_a = (
-            pres_request_a.get("presentation_definition")
-            .input_descriptors[0]
-            .constraint.limit_disclosure
+            pres_request_a.presentation_definition.input_descriptors[
+                0
+            ].constraint.limit_disclosure
         )
         assert test_limit_disclosure_a == "required"
         pres_request_b = DIFProofRequestSchema().load(test_pd_b)
         test_limit_disclosure_b = (
-            pres_request_b.get("presentation_definition")
-            .input_descriptors[0]
-            .constraint.limit_disclosure
+            pres_request_b.presentation_definition.input_descriptors[
+                0
+            ].constraint.limit_disclosure
         )
         assert test_limit_disclosure_b == "preferred"
diff --git a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_routes.py b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_routes.py
--- a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_routes.py
+++ b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_routes.py
@@ -2448,3 +2448,52 @@ async def test_send_presentation_no_specification(self):
         }
         with self.assertRaises(test_module.web.HTTPBadRequest):
             await test_module.present_proof_send_presentation(self.request)
+
+    async def test_v20presentationsendreqschema(self):
+        test_input = {
+            "comment": "string",
+            "connection_id": "631522e9-ca17-4c88-9a4c-d1cad35e463a",
+            "presentation_request": {
+                "dif": {
+                    "_schema": [
+                        {
+                            "uri": "https://www.w3.org/2018/credentials/#VerifiableCredential"
+                        }
+                    ],
+                    "presentation_definition": {
+                        "format": {"ldp_vp": {"proof_type": "BbsBlsSignature2020"}},
+                        "id": "fa2c4a76-c7bd-4313-a0f8-d9f5979c1fd2",
+                        "input_descriptors": [
+                            {
+                                "schema": [
+                                    {
+                                        "uri": "https://www.w3.org/2018/credentials/#VerifiableCredential"
+                                    }
+                                ],
+                                "constraints": {
+                                    "fields": [
+                                        {
+                                            "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+                                            "path": ["$.credentialSubject.id"],
+                                        }
+                                    ],
+                                    "is_holder": [
+                                        {
+                                            "directive": "required",
+                                            "field_id": [
+                                                "3fa85f64-5717-4562-b3fc-2c963f66afa6"
+                                            ],
+                                        }
+                                    ],
+                                    "limit_disclosure": "required",
+                                },
+                                "id": "XXXXXXX",
+                                "name": "XXXXXXX",
+                            }
+                        ],
+                    },
+                }
+            },
+        }
+        with self.assertRaises(TypeError):
+            test_module.V20PresSendRequestRequestSchema.load(test_input)