From 9917d9142fd79e792f85aff06e9dd4419c807fc9 Mon Sep 17 00:00:00 2001 From: Andy McKay Date: Wed, 24 Jul 2013 15:00:02 -0700 Subject: [PATCH] add in product url checks (bug 867282) --- README.md | 5 +++++ receiptverifier.js | 19 ++++++++++++++++++- test.html | 21 ++++++++++++++++++--- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cea3922..0f48f75 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,11 @@ The constructor takes several options: **typsAllowed**: An array of the [receipt types](https://wiki.mozilla.org/Apps/WebApplicationReceipt#the_typ_field) [you'd like to accept](https://developer.mozilla.org/en-US/docs/Apps/Publishing/Validating_a_receipt?redirectlocale=en-US&redirectslug=Apps%2FValidating_a_receipt#What_receipts_you_should_accept). Defaults to accepting developer, reviewer and purchase receipts, but not test receipts. To accept test receipts only, `new mozmarket.receipts.Verifier({typsAllowed: ['test-receipt']...})` +**productURL**: the url that is added to the product in the receipt. For hosted +apps this is the manifest domain, for packaged apps this the origin specified +in the manifest. This is optional, but recommended to ensure that the receipt +issued is for your app. + ### Methods The `mozmarket.receipts.verify()` function is mostly what you'll use. A couple methods you might want from the verifier object: diff --git a/receiptverifier.js b/receiptverifier.js index 56ee2d1..daa3a0a 100644 --- a/receiptverifier.js +++ b/receiptverifier.js @@ -37,6 +37,7 @@ var Verifier = function (options) { this.refundWindow = options.refundWindow || this.defaultRefundWindow; this.installs_allowed_from = options.installs_allowed_from || undefined; this.typsAllowed = options.typsAllowed || ['purchase-receipt', 'developer-receipt', 'reviewer-receipt']; + this.productURL = options.productURL || undefined; this.onlog = options.onlog; if (options.logLevel) { if (typeof options.logLevel == "string") { @@ -178,6 +179,7 @@ Verifier.errors.ConnectionError = Verifier.State("ConnectionError", Verifier.sta Verifier.errors.ReceiptExpired = Verifier.State("ReceiptExpired"); Verifier.errors.VerifyDomainMismatch = Verifier.State("VerifyDomainMismatch"); Verifier.errors.WrongType = Verifier.State("WrongType"); +Verifier.errors.ProductURLError = Verifier.State("ProductURLError"); Verifier.errors.toString = Verifier.states.toString; @@ -186,7 +188,7 @@ Verifier.prototype = { _validConstructorArguments: [ 'cacheStorage', 'cacheTimeout', 'requestTimeout', 'refundWindow', 'installs_allowed_from', 'onlog', - 'logLevel', 'typsAllowed' + 'logLevel', 'typsAllowed', 'productURL' ], defaultCacheTimeout: 1000 * 60 * 60 * 24, // One day @@ -338,6 +340,21 @@ Verifier.prototype = { callback(); return; } + var url = null; + if (this.productURL) { + if (parsed.product && parsed.product.url) { + url = parsed.product.url; + } else { + this._addReceiptError(receipt, new this.errors.ProductURLError("Product URL missing.", {parsed: parsed})); + callback(); + return; + } + if (this.productURL.indexOf(url) < 0) { + this._addReceiptError(receipt, new this.errors.ProductURLError("Product URL wrong: " + url, {parsed: parsed})); + callback(); + return; + } + } var iss = parsed.iss; if (! iss) { this._addReceiptError(receipt, new this.errors.ReceiptFormatError("No (or empty) iss field"), {parsed: parsed}); diff --git a/test.html b/test.html index 515f5da..7a4b6e0 100644 --- a/test.html +++ b/test.html @@ -97,8 +97,8 @@

Do the verification

[...] $ RESPONSE = {data: {status: "ok"}}; $ owaArgs = {requestTimeout: 1000, logLevel: "INFO", onlog: Verifier.consoleLogger} -$ function verify(noClear, waitTime) { -> v = new Verifier(owaArgs); +$ function verify(noClear, waitTime, args) { +> v = new Verifier(args || owaArgs); > if (! noClear) { > v.clearCache(); > } @@ -136,8 +136,23 @@

Exercise failures

$ appData.installData.receipts = [makeReceipt({typ: "test-receipt"})]; $ verify(); verify([Verifier state: [NoValidReceipts ...] Error(...): [WrongType Wrong receipt type...]...]) -$ appData.installData.receipts = [makeReceipt({typ: "purchase-receipt", iss: "http://example.com", verify: "http://example.com/verifier", product: {url: "http://whatever.com"}})]; + +$ appData.installData.receipts = [makeReceipt({typ: "purchase-receipt", iss: "http://example.com", verify: "http://example.com/verifier"})]; +$ verify(true, undefined, {productURL: 'foo', logLevel: "INFO", onlog: Verifier.consoleLogger, requestTimeout: 1000}); +verify([Verifier state: [NoValidReceipts ...] Error(...): [ProductURLError Product URL missing...]...]) + +$ appData.installData.receipts = [makeReceipt({typ: "purchase-receipt", iss: "http://example.com", verify: "http://example.com/verifier", product: {foo: "bar"}})]; +$ verify(true, undefined, {productURL: 'foo', logLevel: "INFO", onlog: Verifier.consoleLogger, requestTimeout: 1000}); +verify([Verifier state: [NoValidReceipts ...] Error(...): [ProductURLError Product URL missing...]...]) + +$ appData.installData.receipts = [makeReceipt({typ: "purchase-receipt", iss: "http://example.com", verify: "http://example.com/verifier", product: {url: "bar"}})]; +$ verify(true, undefined, {productURL: 'foo', logLevel: "INFO", onlog: Verifier.consoleLogger, requestTimeout: 1000}); +verify([Verifier state: [NoValidReceipts ...] Error(...): [ProductURLError Product URL wrong: bar...]...]) + $ appData.manifest.installs_allowed_from = ['http://store.example.com']; +$ verify(true, undefined, {productURL: 'bar', logLevel: "INFO", onlog: Verifier.consoleLogger, requestTimeout: 1000}); +verify([Verifier state: [NoValidReceipts ...] Error(...): [InvalidReceiptIssuer ...]...]) + $ verify(); verify([Verifier state: [NoValidReceipts ...] Error(...): [InvalidReceiptIssuer ...]...]) $ appData.manifest.installs_allowed_from = ['http://store.example.com'];