CVE-2020-11072: Incorrect Comparison (CWE-697)
value: In SLP Validate (npm package slp-validate) before version 1.2.1, users could experience false-negative validation outcomes for MINT transaction operations. A poorly implemented SLP wallet could allow spending of the affected tokens which would result in the destruction of a user's minting baton. This has been fixed in slp-validate in version 1.2.1. Additonally, slpjs version 0.27.2 has a related fix under related CVE-2020-11071.
CWE description: The product compares two entities in a security-relevant context, but the comparison is incorrect, which may lead to resultant weaknesses.
CVSS v3 base score: 8.6
Published: 2020-05-12T01:15Z
Repository: https://github.com/simpleledger/slp-validate
Language: TypeScript
Files changed: lib/validation.ts
Commit date: 2020-04-29 09:52:56 -0400
Commit message: fix false negative case for MINT transactions

See the newly added unit test case for this issue.
Currently there are 151 unit tests.

Diff: diff --git a/lib/validation.ts b/lib/validation.ts
index 8e4b5f1..248dd7d 100644
--- a/lib/validation.ts
+++ b/lib/validation.ts
@@ -406,14 +406,14 @@ export class ValidatorType1 {
                             }
                         }
                     }
                 } catch (_) { }
             }
-            if (validation.parents.length !== 1) {
+            if (validation.parents.length < 1) {
                 validation.validity = false;
                 validation.waiting = false;
-                validation.invalidReason = "MINT transaction must have 1 valid baton parent.";
+                validation.invalidReason = "MINT transaction must have at least 1 candidate baton parent input.";
                 return validation.validity!;
             }
         } else if (slpmsg.transactionType === SlpTransactionType.SEND) {
             const tokenOutQty = slpmsg.sendOutputs!.reduce((t, v) => t.plus(v), new Big(0));
             let tokenInQty = new Big(0);
@@ -466,14 +466,18 @@ export class ValidatorType1 {
         }
 
         // Set validity validation-cache for parents, and handle MINT condition with no valid input
         // we don't need to check proper token id since we only added parents with same ID in above steps.
         const parentTxids = [...new Set(validation.parents.map(p => p.txid))];
-        for (let i = 0; i < parentTxids.length; i++) {
-            const valid = await this.isValidSlpTxid({ txid: parentTxids[i] });
-            validation.parents.filter(p => p.txid === parentTxids[i]).map(p => p.valid = valid);
-            if (validation.details!.transactionType === SlpTransactionType.MINT && !valid) {
+        for (const id of parentTxids) {
+            const valid = await this.isValidSlpTxid({ txid: id });
+            validation.parents.filter(p => p.txid === id).map(p => p.valid = valid);
+        }
+
+        // Check MINT for exactly 1 valid MINT baton
+        if (validation.details!.transactionType === SlpTransactionType.MINT) {
+            if (validation.parents.filter(p => p.valid && p.inputQty === null).length !== 1) {
                 validation.validity = false;
                 validation.waiting = false;
                 validation.invalidReason = "MINT transaction with invalid baton parent.";
                 return validation.validity!;
             }

Vulnerable code:             if (validation.parents.length !== 1) {
                validation.invalidReason = "MINT transaction must have 1 valid baton parent.";
        for (let i = 0; i < parentTxids.length; i++) {
            const valid = await this.isValidSlpTxid({ txid: parentTxids[i] });
            validation.parents.filter(p => p.txid === parentTxids[i]).map(p => p.valid = valid);
            if (validation.details!.transactionType === SlpTransactionType.MINT && !valid) {
Fixed code:             if (validation.parents.length < 1) {
                validation.invalidReason = "MINT transaction must have at least 1 candidate baton parent input.";
        for (const id of parentTxids) {
            const valid = await this.isValidSlpTxid({ txid: id });
            validation.parents.filter(p => p.txid === id).map(p => p.valid = valid);
        }

        // Check MINT for exactly 1 valid MINT baton
        if (validation.details!.transactionType === SlpTransactionType.MINT) {
            if (validation.parents.filter(p => p.valid && p.inputQty === null).length !== 1) {