CVE-2020-15130: Incorrect Comparison (CWE-697)
value: In SLPJS (npm package slpjs) before version 0.27.4, 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 0.27.4.
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/slpjs
Language: JSON
Files changed: lib/localvalidator.ts, package-lock.json, package.json
Commit date: 2020-07-29 12:10:41 -0400
Commit message: critical update for NFT child genesis validation

Diff: diff --git a/lib/localvalidator.ts b/lib/localvalidator.ts
index fd2607d..7d56222 100644
--- a/lib/localvalidator.ts
+++ b/lib/localvalidator.ts
@@ -208,13 +208,14 @@ export class LocalValidator implements SlpValidator {
         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
-                let input_txid = txn.inputs[0].prevTxId.toString("hex");
-                let input_txhex = await this.retrieveRawTransaction(input_txid);
-                let input_tx: Bitcore.Transaction = new Bitcore.Transaction(input_txhex);
+                const input_txid = txn.inputs[0].prevTxId.toString("hex");
+                const input_prevout = txn.inputs[0].outputIndex;
+                const input_txhex = await this.retrieveRawTransaction(input_txid);
+                const input_tx: Bitcore.Transaction = new Bitcore.Transaction(input_txhex);
                 let input_slpmsg;
                 try {
                     input_slpmsg = this.slp.parseSlpOutputScript(input_tx.outputs[0]._scriptBuffer);
                 } catch (_) { }
                 if (!input_slpmsg || input_slpmsg.versionType !== 0x81) {
@@ -222,25 +223,38 @@ export class LocalValidator implements SlpValidator {
                     this.cachedValidations[txid].waiting = false;
                     this.cachedValidations[txid].invalidReason = "NFT1 child GENESIS does not have a valid NFT1 parent input.";
                     return this.cachedValidations[txid].validity!;
                 }
                 // Check that the there is a burned output >0 in the parent txn SLP message
-                if (input_slpmsg.transactionType === SlpTransactionType.SEND &&
-                    (!input_slpmsg.sendOutputs![1].isGreaterThan(0)))
-                {
-                    this.cachedValidations[txid].validity = false;
-                    this.cachedValidations[txid].waiting = false;
-                    this.cachedValidations[txid].invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
-                    return this.cachedValidations[txid].validity!;
-                } else if ((input_slpmsg.transactionType === SlpTransactionType.GENESIS ||
-                            input_slpmsg.transactionType === SlpTransactionType.MINT) &&
-                            !input_slpmsg.genesisOrMintQuantity!.isGreaterThan(0))
-                {
-                    this.cachedValidations[txid].validity = false;
-                    this.cachedValidations[txid].waiting = false;
-                    this.cachedValidations[txid].invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
-                    return this.cachedValidations[txid].validity!;
+                if (input_slpmsg.transactionType === SlpTransactionType.SEND) {
+                    if (input_prevout > input_slpmsg.sendOutputs!.length - 1) {
+                        this.cachedValidations[txid].validity = false;
+                        this.cachedValidations[txid].waiting = false;
+                        this.cachedValidations[txid].invalidReason = "NFT1 child GENESIS does not have a valid NFT1 parent input.";
+                        return this.cachedValidations[txid].validity!;
+                    } else if (! input_slpmsg.sendOutputs![input_prevout].isGreaterThan(0)) {
+                        this.cachedValidations[txid].validity = false;
+                        this.cachedValidations[txid].waiting = false;
+                        this.cachedValidations[txid].invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
+                        return this.cachedValidations[txid].validity!;
+                    } else {
+                        this.cachedValidations[txid].validity = true;
+                        this.cachedValidations[txid].waiting = false;
+                    }
+                } else if (input_slpmsg.transactionType === SlpTransactionType.GENESIS ||
+                            input_slpmsg.transactionType === SlpTransactionType.MINT) {
+                    if (input_prevout !== 1) {
+                        this.cachedValidations[txid].validity = false;
+                        this.cachedValidations[txid].waiting = false;
+                        this.cachedValidations[txid].invalidReason = "NFT1 child GENESIS does not have a valid NFT1 parent input.";
+                        return this.cachedValidations[txid].validity!;
+                    } else if (!input_slpmsg.genesisOrMintQuantity!.isGreaterThan(0)) {
+                        this.cachedValidations[txid].validity = false;
+                        this.cachedValidations[txid].waiting = false;
+                        this.cachedValidations[txid].invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
+                        return this.cachedValidations[txid].validity!;
+                    }
                 }
                 // Continue to check the NFT1 parent DAG
                 let nft_parent_dag_validity = await this.isValidSlpTxid(input_txid, undefined, 0x81);
                 this.cachedValidations[txid].validity = nft_parent_dag_validity;
                 this.cachedValidations[txid].waiting = false;
diff --git a/package-lock.json b/package-lock.json
index 21dfe63..b1ba8ed 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5590,12 +5590,12 @@
       "requires": {
         "bignumber.js": "^9.0.0"
       }
     },
     "slp-unit-test-data": {
-      "version": "git+https://github.com/simpleledger/slp-unit-test-data.git#a450146e112aee4db5a1b33dc978229fdcfb23a4",
-      "from": "git+https://github.com/simpleledger/slp-unit-test-data.git#a450146e112aee4db5a1b33dc978229fdcfb23a4",
+      "version": "git+https://github.com/simpleledger/slp-unit-test-data.git#8c942eacfae12686dcf1f3366321445a4fba73e7",
+      "from": "git+https://github.com/simpleledger/slp-unit-test-data.git#8c942eacfae12686dcf1f3366321445a4fba73e7",
       "dev": true
     },
     "socket.io": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz",
diff --git a/package.json b/package.json
index c8063a3..0ec3bdf 100644
--- a/package.json
+++ b/package.json
@@ -42,11 +42,11 @@
     "bitcore-lib-cash": "^9.0.0",
     "browserify": "^16.5.1",
     "mkdirp": "^0.5.5",
     "mocha": "^7.2.0",
     "nyc": "^15.1.0",
-    "slp-unit-test-data": "git+https://github.com/simpleledger/slp-unit-test-data.git#a450146e112aee4db5a1b33dc978229fdcfb23a4",
+    "slp-unit-test-data": "git+https://github.com/simpleledger/slp-unit-test-data.git#8c942eacfae12686dcf1f3366321445a4fba73e7",
     "source-map-support": "^0.5.19",
     "ts-node": "^7.0.1",
     "typescript": "^3.9.5",
     "typescript-tslint-plugin": "^0.5.5",
     "uglify-es": "^3.3.9"

Vulnerable code:                 let input_txid = txn.inputs[0].prevTxId.toString("hex");
                let input_txhex = await this.retrieveRawTransaction(input_txid);
                let input_tx: Bitcore.Transaction = new Bitcore.Transaction(input_txhex);
                if (input_slpmsg.transactionType === SlpTransactionType.SEND &&
                    (!input_slpmsg.sendOutputs![1].isGreaterThan(0)))
                {
                    this.cachedValidations[txid].validity = false;
                    this.cachedValidations[txid].waiting = false;
                    this.cachedValidations[txid].invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
                    return this.cachedValidations[txid].validity!;
                } else if ((input_slpmsg.transactionType === SlpTransactionType.GENESIS ||
                            input_slpmsg.transactionType === SlpTransactionType.MINT) &&
                            !input_slpmsg.genesisOrMintQuantity!.isGreaterThan(0))
                {
                    this.cachedValidations[txid].validity = false;
                    this.cachedValidations[txid].waiting = false;
                    this.cachedValidations[txid].invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
                    return this.cachedValidations[txid].validity!;
      "version": "git+https://github.com/simpleledger/slp-unit-test-data.git#a450146e112aee4db5a1b33dc978229fdcfb23a4",
      "from": "git+https://github.com/simpleledger/slp-unit-test-data.git#a450146e112aee4db5a1b33dc978229fdcfb23a4",
    "slp-unit-test-data": "git+https://github.com/simpleledger/slp-unit-test-data.git#a450146e112aee4db5a1b33dc978229fdcfb23a4",
Fixed code:                 const input_txid = txn.inputs[0].prevTxId.toString("hex");
                const input_prevout = txn.inputs[0].outputIndex;
                const input_txhex = await this.retrieveRawTransaction(input_txid);
                const input_tx: Bitcore.Transaction = new Bitcore.Transaction(input_txhex);
                if (input_slpmsg.transactionType === SlpTransactionType.SEND) {
                    if (input_prevout > input_slpmsg.sendOutputs!.length - 1) {
                        this.cachedValidations[txid].validity = false;
                        this.cachedValidations[txid].waiting = false;
                        this.cachedValidations[txid].invalidReason = "NFT1 child GENESIS does not have a valid NFT1 parent input.";
                        return this.cachedValidations[txid].validity!;
                    } else if (! input_slpmsg.sendOutputs![input_prevout].isGreaterThan(0)) {
                        this.cachedValidations[txid].validity = false;
                        this.cachedValidations[txid].waiting = false;
                        this.cachedValidations[txid].invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
                        return this.cachedValidations[txid].validity!;
                    } else {
                        this.cachedValidations[txid].validity = true;
                        this.cachedValidations[txid].waiting = false;
                    }
                } else if (input_slpmsg.transactionType === SlpTransactionType.GENESIS ||
                            input_slpmsg.transactionType === SlpTransactionType.MINT) {
                    if (input_prevout !== 1) {
                        this.cachedValidations[txid].validity = false;
                        this.cachedValidations[txid].waiting = false;
                        this.cachedValidations[txid].invalidReason = "NFT1 child GENESIS does not have a valid NFT1 parent input.";
                        return this.cachedValidations[txid].validity!;
                    } else if (!input_slpmsg.genesisOrMintQuantity!.isGreaterThan(0)) {
                        this.cachedValidations[txid].validity = false;
                        this.cachedValidations[txid].waiting = false;
                        this.cachedValidations[txid].invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
                        return this.cachedValidations[txid].validity!;
                    }
      "version": "git+https://github.com/simpleledger/slp-unit-test-data.git#8c942eacfae12686dcf1f3366321445a4fba73e7",
      "from": "git+https://github.com/simpleledger/slp-unit-test-data.git#8c942eacfae12686dcf1f3366321445a4fba73e7",
    "slp-unit-test-data": "git+https://github.com/simpleledger/slp-unit-test-data.git#8c942eacfae12686dcf1f3366321445a4fba73e7",