commit 1d17a50c4d5858e737f9f156db664cc0b5772836
parent d34edb354a0dca17069a29ed61561d1765f60fc9
Author: Christian Grothoff <christian@grothoff.org>
Date: Sun, 14 Dec 2025 15:08:59 +0100
enforce FORM_ID is present in all attributes (#10757)
Diffstat:
14 files changed, 136 insertions(+), 10 deletions(-)
diff --git a/src/exchange/taler-exchange-httpd_aml-decision.c b/src/exchange/taler-exchange-httpd_aml-decision.c
@@ -273,6 +273,18 @@ TEH_handler_post_aml_decision (
ret = MHD_YES /* failure */;
goto done;
}
+ if ( (NULL != attributes) &&
+ (! json_is_string (json_object_get (attributes,
+ "FORM_ID"))) )
+ {
+ GNUNET_break_op (0);
+ ret = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "attributes['FORM_ID']");
+ goto done;
+ }
}
if (NULL != payto_uri.full_payto)
{
diff --git a/src/exchange/taler-exchange-httpd_common_kyc.c b/src/exchange/taler-exchange-httpd_common_kyc.c
@@ -337,6 +337,7 @@ TEH_kyc_store_attributes (
{
form_id = json_string_value (json_object_get (new_attributes,
"FORM_ID"));
+ GNUNET_assert (NULL != form_id);
TALER_CRYPTO_kyc_attributes_encrypt (&TEH_attribute_key,
new_attributes,
&ea,
diff --git a/src/exchange/taler-exchange-httpd_config.h b/src/exchange/taler-exchange-httpd_config.h
@@ -41,7 +41,7 @@
*
* Returned via both /config and /keys endpoints.
*/
-#define EXCHANGE_PROTOCOL_VERSION "31:0:9"
+#define EXCHANGE_PROTOCOL_VERSION "31:1:9"
/**
diff --git a/src/exchange/taler-exchange-httpd_kyc-upload.c b/src/exchange/taler-exchange-httpd_kyc-upload.c
@@ -496,6 +496,17 @@ TEH_handler_kyc_upload (
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"/kyc-upload received form submission\n");
+ if ( (NULL != root) &&
+ (! json_is_string (json_object_get (root,
+ "FORM_ID"))) )
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "FORM_ID");
+ }
json_dumpf (root,
stderr,
JSON_INDENT (2));
diff --git a/src/exchange/taler-exchange-httpd_reserves_attest.c b/src/exchange/taler-exchange-httpd_reserves_attest.c
@@ -190,7 +190,8 @@ kyc_process_cb (void *cls,
}
json_object_foreach (attrs, name, val)
{
- bool requested = false;
+ bool requested = strcmp (name,
+ "FORM_ID"); /* we always return the FORM_ID */
size_t idx;
json_t *str;
diff --git a/src/include/taler/taler_kyclogic_plugin.h b/src/include/taler/taler_kyclogic_plugin.h
@@ -164,7 +164,7 @@ typedef void
* @param provider_name name of the provider with the attributes
* @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown
* @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown
- * @param attributes user attributes returned by the provider
+ * @param attributes user attributes returned by the provider, MUST include "FORM_ID" of type String, or NULL on error
* @param expiration until when is the KYC check valid
* @param http_status HTTP status code of @a response
* @param[in] response to return to the HTTP client
@@ -197,7 +197,7 @@ typedef void
* @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown
* @param status KYC status
* @param expiration until when is the KYC check valid
- * @param attributes user attributes returned by the provider
+ * @param attributes user attributes returned by the provider, MUST include "FORM_ID" of type String (or NULL on error)
* @param http_status HTTP status code of @a response
* @param[in] response to return to the HTTP client
*/
diff --git a/src/kyclogic/plugin_kyclogic_kycaid.c b/src/kyclogic/plugin_kyclogic_kycaid.c
@@ -903,6 +903,36 @@ webhook_conversion_cb (void *cls,
kycaid_webhook_cancel (wh);
return;
}
+ if (! json_is_string (json_object_get (result,
+ "FORM_ID")))
+ {
+ /* Failure in our helper */
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Mandatory FORM_ID not set in result\n");
+ json_dumpf (result,
+ stderr,
+ JSON_INDENT (2));
+ resp = TALER_MHD_MAKE_JSON_PACK (
+ GNUNET_JSON_pack_uint64 ("kycaid_http_status",
+ wh->kycaid_response_code),
+ GNUNET_JSON_pack_object_incref ("kycaid_body",
+ (json_t *) wh->json_response));
+ wh->cb (wh->cb_cls,
+ wh->process_row,
+ &wh->h_payto,
+ wh->is_wallet,
+ wh->pd->section,
+ wh->applicant_id,
+ wh->verification_id,
+ TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
+ GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
+ NULL,
+ MHD_HTTP_BAD_GATEWAY,
+ resp);
+ kycaid_webhook_cancel (wh);
+ return;
+ }
+
expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity);
resp = MHD_create_response_from_buffer_static (0,
"");
diff --git a/src/kyclogic/plugin_kyclogic_oauth2.c b/src/kyclogic/plugin_kyclogic_oauth2.c
@@ -1181,6 +1181,39 @@ converted_proof_cb (void *cls,
}
ph->provider_user_id = GNUNET_strdup (id);
}
+ if (! json_is_string (json_object_get (attr,
+ "FORM_ID")))
+ {
+ json_t *body;
+
+ GNUNET_break_op (0);
+ ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;
+ ph->http_status = MHD_HTTP_BAD_GATEWAY;
+ body = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("converter",
+ pd->conversion_binary),
+ GNUNET_JSON_pack_string ("message",
+ "Missing 'FORM_ID' field in attributes"),
+ GNUNET_JSON_pack_bool ("debug",
+ ph->pd->debug_mode),
+ GNUNET_JSON_pack_object_incref ("attributes",
+ (json_t *) attr),
+ TALER_JSON_pack_ec (
+ TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE));
+ GNUNET_break (
+ GNUNET_SYSERR !=
+ TALER_TEMPLATING_build (ph->connection,
+ &ph->http_status,
+ "oauth2-conversion-failure",
+ NULL,
+ NULL,
+ body,
+ &ph->response));
+ json_decref (body);
+ ph->task = GNUNET_SCHEDULER_add_now (&return_proof_response,
+ ph);
+ return;
+ }
ph->status = TALER_KYCLOGIC_STATUS_SUCCESS;
ph->response = MHD_create_response_from_buffer_static (0,
"");
diff --git a/src/kyclogic/plugin_kyclogic_persona.c b/src/kyclogic/plugin_kyclogic_persona.c
@@ -354,6 +354,11 @@ struct TALER_KYCLOGIC_WebhookHandle
uint64_t process_row;
/**
+ * HTTP status returned by Persona to us.
+ */
+ unsigned int persona_http_status;
+
+ /**
* HTTP response code to return asynchronously.
*/
unsigned int response_code;
@@ -1635,6 +1640,38 @@ webhook_post_conversion_cb (void *cls,
struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
wh->ec = NULL;
+ if (! json_is_string (json_object_get (attr,
+ "FORM_ID")))
+ {
+ struct MHD_Response *resp;
+
+ /* Failure in our helper */
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Mandatory FORM_ID not set in result\n");
+ json_dumpf (attr,
+ stderr,
+ JSON_INDENT (2));
+ resp = TALER_MHD_MAKE_JSON_PACK (
+ GNUNET_JSON_pack_uint64 ("persona_http_status",
+ wh->persona_http_status),
+ GNUNET_JSON_pack_object_incref ("persona_body",
+ (json_t *) attr));
+ wh->cb (wh->cb_cls,
+ wh->process_row,
+ &wh->h_payto,
+ wh->is_wallet,
+ wh->pd->section,
+ NULL,
+ wh->inquiry_id,
+ TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
+ GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
+ NULL,
+ MHD_HTTP_BAD_GATEWAY,
+ resp);
+ persona_webhook_cancel (wh);
+ return;
+ }
+
webhook_generic_reply (wh,
TALER_KYCLOGIC_STATUS_SUCCESS,
wh->account_id,
@@ -1663,6 +1700,7 @@ handle_webhook_finished (void *cls,
"data");
wh->job = NULL;
+ wh->persona_http_status = response_code;
switch (response_code)
{
case MHD_HTTP_OK:
diff --git a/src/kyclogic/taler-exchange-kyc-kycaid-converter.sh b/src/kyclogic/taler-exchange-kyc-kycaid-converter.sh
@@ -73,7 +73,7 @@ then
echo "$J" \
| jq \
--arg full_name "${FULLNAME}" \
- '{$full_name,"birthdate":.dob,"pep":.pep,"phone":.phone,"email":.email,"residences":.residence_country}' \
+ '{"FORM_ID":"kycaid-individual",$full_name,"birthdate":.dob,"pep":.pep,"phone":.phone,"email":.email,"residences":.residence_country}' \
| jq \
'del(..|select(.==null))'
@@ -82,7 +82,7 @@ else
echo "$J" \
| jq \
"$DOCS_RAW" \
- "{\"company_name\":.company_name,\"phone\":.phone,\"email\":.email,\"registration_country\":.registration_country,\"documents\":[${DOCS_JSON}]}" \
+ '{"FORM_ID":"kycaid-business","company_name":.company_name,"phone":.phone,"email":.email,"registration_country":.registration_country,"documents":['${DOCS_JSON}']}' \
| jq \
'del(..|select(.==null))'
fi
diff --git a/src/kyclogic/taler-exchange-kyc-oauth2-challenger.sh b/src/kyclogic/taler-exchange-kyc-oauth2-challenger.sh
@@ -22,6 +22,6 @@ fi
echo "$J" \
| jq \
--arg id "${ROWID}" \
- '{$id,"email":.email,"expires":.expires}'
+ '{"FORM_ID":"challenger-email",$id,"email":.email,"expires":.expires}'
exit 0
diff --git a/src/kyclogic/taler-exchange-kyc-oauth2-nda.sh b/src/kyclogic/taler-exchange-kyc-oauth2-nda.sh
@@ -23,7 +23,7 @@ FULLNAME=$(echo "$J" | jq -r '[.first_name,.last_name]|join(" ")')
echo "$J" \
| jq \
--arg full_name "${FULLNAME}" \
- '{FULL_NAME: $full_name, "phone": .phone, "id": .id}' \
+ '{"FORM_ID": "oauth2-nda-form","FULL_NAME": $full_name, "phone": .phone, "id": .id}' \
| jq \
'del(..|select(.==null))'
diff --git a/src/kyclogic/taler-exchange-kyc-oauth2-test-converter.sh b/src/kyclogic/taler-exchange-kyc-oauth2-test-converter.sh
@@ -26,7 +26,7 @@ FULLNAME=$(echo "$J" | jq -r '[.first,.last]|join(" ")')
echo "$J" \
| jq \
--arg full_name "${FULLNAME}" \
- '{FORM_ID:"test",FULL_NAME: $full_name,"DATE_OF_BIRTH":.birthdate,"id":.id,}' \
+ '{"FORM_ID":"test","FULL_NAME": $full_name,"DATE_OF_BIRTH":.birthdate,"id":.id,}' \
| jq \
'del(..|select(.==null))'
exit 0
diff --git a/src/kyclogic/taler-exchange-kyc-persona-converter.sh b/src/kyclogic/taler-exchange-kyc-persona-converter.sh
@@ -50,7 +50,7 @@ echo "$J" \
--arg street "${STREET}" \
--arg city "${CITY}" \
--rawfile photo "${PHOTO_FILE}" \
- '{FULL_NAME: $full_name, $street, $city, DATE_OF_BIRTH: .birthdate, "residences":.cc, "identification_number": ."identification-number", $photo}' \
+ '{"FORM_ID":"persona-individual",FULL_NAME: $full_name, $street, $city, DATE_OF_BIRTH: .birthdate, "residences":.cc, "identification_number": ."identification-number", $photo}' \
| jq \
'del(..|select(.==null))'