CVE-2020-15131: Incorrect Comparison (CWE-697)
value: In SLP Validate (npm package slp-validate) before version 1.2.2, there is a vulnerability to false-positive validation outcomes for the NFT1 Child Genesis transaction type. A poorly implemented SLP wallet or opportunistic attacker could create a seemingly valid NFT1 child token without burning any of the NFT1 Group token type as is required by the NFT1 specification. This is fixed in version 1.2.2.
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: 7.5
Published: 2020-07-30T15:15Z
Repository: https://github.com/simpleledger/slp-validate.js
Language: JSON
Files changed: lib/validation.ts, package-lock.json
Commit date: 2020-07-29 11:53:29 -0400
Commit message: critical update for NFT child genesis validation

Diff: diff --git a/lib/validation.ts b/lib/validation.ts
index bc9ae2d..cf41d3a 100644
--- a/lib/validation.ts
+++ b/lib/validation.ts
@@ -344,41 +344,57 @@ export class ValidatorType1 {
         if (slpmsg.transactionType === SlpTransactionType.GENESIS) {
             // Check for NFT1 child (type 0x41)
             if (slpmsg.versionType === 0x41) {
                 // An NFT1 parent should be provided at input index 0,
                 // so we check this first before checking the whole parent DAG
-                const inputTxid = txn.inputs[0].previousTxHash;
-                const inputTxHex = await this.retrieveRawTransaction(inputTxid);
-                const inputTx: Transaction = Transaction.parseFromBuffer(inputTxHex);
+                const inputPrevTxid = txn.inputs[0].previousTxHash;
+                const inputPrevOut = txn.inputs[0].previousTxOutIndex;
+                const inputPrevTxHex = await this.retrieveRawTransaction(inputPrevTxid);
+                const inputPrevTx: Transaction = Transaction.parseFromBuffer(inputPrevTxHex);
                 let inputSlpMsg;
                 try {
-                    inputSlpMsg = Slp.parseSlpOutputScript(inputTx.outputs[0].scriptPubKey);
+                    inputSlpMsg = Slp.parseSlpOutputScript(inputPrevTx.outputs[0].scriptPubKey);
                 } catch (_) {}
                 if (!inputSlpMsg || inputSlpMsg.versionType !== 0x81) {
                     validation.validity = false;
                     validation.waiting = false;
                     validation.invalidReason = "NFT1 child GENESIS does not have a valid NFT1 parent input.";
                     return validation.validity!;
                 }
                 // Check that the there is a burned output >0 in the parent txn SLP message
-                if (inputSlpMsg.transactionType === SlpTransactionType.SEND &&
-                    !inputSlpMsg.sendOutputs![1].gt(0)) {
-                    validation.validity = false;
-                    validation.waiting = false;
-                    validation.invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
-                    return validation.validity!;
-                } else if ((inputSlpMsg.transactionType === SlpTransactionType.GENESIS ||
-                            inputSlpMsg.transactionType === SlpTransactionType.MINT) &&
-                            !inputSlpMsg.genesisOrMintQuantity!.gt(0)) {
-                    validation.validity = false;
-                    validation.waiting = false;
-                    validation.invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
-                    return validation.validity!;
+                if (inputSlpMsg.transactionType === SlpTransactionType.SEND) {
+                    if (inputPrevOut > inputSlpMsg.sendOutputs!.length - 1) {
+                        validation.validity = false;
+                        validation.waiting = false;
+                        validation.invalidReason = "NFT1 child GENESIS does not have a valid NFT1 parent input.";
+                        return validation.validity!;
+                    }
+                    if (! inputSlpMsg.sendOutputs![inputPrevOut].gt(0)) {
+                        validation.validity = false;
+                        validation.waiting = false;
+                        validation.invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
+                        return validation.validity!;
+                    }
+                } else if (inputSlpMsg.transactionType === SlpTransactionType.GENESIS ||
+                            inputSlpMsg.transactionType === SlpTransactionType.MINT) {
+                    if (inputPrevOut !== 1) {
+                        validation.validity = false;
+                        validation.waiting = false;
+                        validation.invalidReason = "NFT1 child GENESIS does not have a valid NFT1 parent input.";
+                        return validation.validity!;
+                    }
+                    if (!inputSlpMsg.genesisOrMintQuantity!.gt(0)) {
+                        validation.validity = false;
+                        validation.waiting = false;
+                        validation.invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
+                        return validation.validity!;
+                    }
                 }
+
                 // Continue to check the NFT1 parent DAG
                 const nft_parent_dag_validity = await this.isValidSlpTxid({
-                    txid: inputTxid,
+                    txid: inputPrevTxid,
                     tokenIdFilter: undefined,
                     tokenTypeFilter: 0x81
                 });
                 validation.validity = nft_parent_dag_validity;
                 validation.waiting = false;
diff --git a/package-lock.json b/package-lock.json
index 36fda69..2158e80 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4085,11 +4085,11 @@
       "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
       "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=",
       "dev": true
     },
     "slp-unit-test-data": {
-      "version": "git+https://github.com/simpleledger/slp-unit-test-data.git#be74a6005dbf7dfabce19a9f920b2632b539f73e",
+      "version": "git+https://github.com/simpleledger/slp-unit-test-data.git#8c942eacfae12686dcf1f3366321445a4fba73e7",
       "from": "git+https://github.com/simpleledger/slp-unit-test-data.git",
       "dev": true
     },
     "source-map": {
       "version": "0.5.7",

Vulnerable code:                 const inputTxid = txn.inputs[0].previousTxHash;
                const inputTxHex = await this.retrieveRawTransaction(inputTxid);
                const inputTx: Transaction = Transaction.parseFromBuffer(inputTxHex);
                    inputSlpMsg = Slp.parseSlpOutputScript(inputTx.outputs[0].scriptPubKey);
                if (inputSlpMsg.transactionType === SlpTransactionType.SEND &&
                    !inputSlpMsg.sendOutputs![1].gt(0)) {
                    validation.validity = false;
                    validation.waiting = false;
                    validation.invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
                    return validation.validity!;
                } else if ((inputSlpMsg.transactionType === SlpTransactionType.GENESIS ||
                            inputSlpMsg.transactionType === SlpTransactionType.MINT) &&
                            !inputSlpMsg.genesisOrMintQuantity!.gt(0)) {
                    validation.validity = false;
                    validation.waiting = false;
                    validation.invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
                    return validation.validity!;
                    txid: inputTxid,
      "version": "git+https://github.com/simpleledger/slp-unit-test-data.git#be74a6005dbf7dfabce19a9f920b2632b539f73e",
Fixed code:                 const inputPrevTxid = txn.inputs[0].previousTxHash;
                const inputPrevOut = txn.inputs[0].previousTxOutIndex;
                const inputPrevTxHex = await this.retrieveRawTransaction(inputPrevTxid);
                const inputPrevTx: Transaction = Transaction.parseFromBuffer(inputPrevTxHex);
                    inputSlpMsg = Slp.parseSlpOutputScript(inputPrevTx.outputs[0].scriptPubKey);
                if (inputSlpMsg.transactionType === SlpTransactionType.SEND) {
                    if (inputPrevOut > inputSlpMsg.sendOutputs!.length - 1) {
                        validation.validity = false;
                        validation.waiting = false;
                        validation.invalidReason = "NFT1 child GENESIS does not have a valid NFT1 parent input.";
                        return validation.validity!;
                    }
                    if (! inputSlpMsg.sendOutputs![inputPrevOut].gt(0)) {
                        validation.validity = false;
                        validation.waiting = false;
                        validation.invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
                        return validation.validity!;
                    }
                } else if (inputSlpMsg.transactionType === SlpTransactionType.GENESIS ||
                            inputSlpMsg.transactionType === SlpTransactionType.MINT) {
                    if (inputPrevOut !== 1) {
                        validation.validity = false;
                        validation.waiting = false;
                        validation.invalidReason = "NFT1 child GENESIS does not have a valid NFT1 parent input.";
                        return validation.validity!;
                    }
                    if (!inputSlpMsg.genesisOrMintQuantity!.gt(0)) {
                        validation.validity = false;
                        validation.waiting = false;
                        validation.invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
                        return validation.validity!;
                    }

                    txid: inputPrevTxid,
      "version": "git+https://github.com/simpleledger/slp-unit-test-data.git#8c942eacfae12686dcf1f3366321445a4fba73e7",