fix(ink/credential): cota anti-griefing del Vec del holder (audit C, HIGH DoS)

Audit ultra 2026-07-04 (dimensión ink-contracts, decisión C de David): un issuer
malicioso podía emitir credenciales ilimitadas a una VÍCTIMA, bloateando su
holder_credentials: Vec hasta que cualquier op que lo cargara (issue/read)
excedía el gas → griefing DoS. Fix: MAX_CREDENTIALS_PER_HOLDER=512 + error
HolderCredentialLimitReached antes del push. (Nota: el índice issuer_credentials
de un emisor de alto volumen debe migrar a Mapping<(issuer,i),id> paginado —
trabajo futuro; no es griefing de terceros.) Contrato 4 tests verde.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
davidherranandClaude Fable 5 committed 2026-07-05 20:05:12 -05:00
1 parent 9f48723ca5
commit 114721c24a
1 file changed
+18
+18
View File
@@ -147,8 +147,21 @@ mod origin_credential_mint {
AlreadyExpired,
InvalidConfiguration,
TransferFailed,
/// Audit 2026-07-04: el holder alcanzó el máximo de credenciales
/// (`MAX_CREDENTIALS_PER_HOLDER`) — cota anti-griefing/DoS. Sin esto, un
/// issuer malicioso bloateaba el `Vec` del holder hasta que cualquier
/// operación que lo cargue (issue/read) excedía el gas.
HolderCredentialLimitReached,
}
/// Máximo de credenciales por holder (cota anti-griefing del `Vec` en storage).
/// Una persona/entidad tiene un número acotado de credenciales legítimas; el
/// límite evita que un issuer malicioso infle el `Vec` de una víctima hasta
/// hacerlo ilegible por gas. (El índice `issuer_credentials` de un emisor de
/// alto volumen debe migrar a un índice paginado `Mapping<(issuer,i),id>` —
/// trabajo futuro; no es un vector de griefing de terceros.)
pub const MAX_CREDENTIALS_PER_HOLDER: u32 = 512;
pub type Result<T> = core::result::Result<T, Error>;
/// Convierte un valor `U256` del entorno pallet-revive a `Balance` (u128)
@@ -289,6 +302,11 @@ mod origin_credential_mint {
// Update indices canonical
let mut holder_list = self.holder_credentials.get(holder).unwrap_or_default();
// Audit 2026-07-04: cota anti-griefing — sin esto un issuer malicioso
// bloatea el Vec del holder hasta hacerlo ilegible por gas.
if holder_list.len() as u32 >= MAX_CREDENTIALS_PER_HOLDER {
return Err(Error::HolderCredentialLimitReached);
}
holder_list.push(id);
self.holder_credentials.insert(holder, &holder_list);