diff --git a/packages/server/test/unit/blacklist_spec.coffee b/packages/server/test/unit/blacklist_spec.coffee deleted file mode 100644 index f0796cea355d..000000000000 --- a/packages/server/test/unit/blacklist_spec.coffee +++ /dev/null @@ -1,45 +0,0 @@ -require("../spec_helper") - -blacklist = require("#{root}lib/util/blacklist") - -hosts = [ - "*.google.com" - "shop.apple.com" - "localhost:6666" - "adwords.com" - "*yahoo.com" -] - -matchesStr = (url, host, val) -> - m = blacklist.matches(url, host) - expect(!!m).to.eq(val, "url: '#{url}' did not pass") - -matchesArray = (url, val) -> - m = blacklist.matches(url, hosts) - expect(!!m).to.eq(val, "url: '#{url}' did not pass") - -matchesHost = (url, host) -> - expect(blacklist.matches(url, hosts)).to.eq(host) - -describe "lib/util/blacklist", -> - it "handles hosts, ports, wildcards", -> - matchesArray("https://mail.google.com/foo", true) - matchesArray("https://shop.apple.com/bar", true) - matchesArray("http://localhost:6666/", true) - matchesArray("https://localhost:6666/", true) - matchesArray("https://adwords.com:443/", true) - matchesArray("http://adwords.com:80/quux", true) - matchesArray("https://yahoo.com:443/asdf", true) - matchesArray("http://mail.yahoo.com:443/asdf", true) - - matchesArray("https://buy.adwords.com:443/", false) - matchesArray("http://localhost:66667", false) - matchesArray("http://mac.apple.com/", false) - - matchesStr("https://localhost:6666/foo", "localhost:6666", true) - matchesStr("https://localhost:6666/foo", "localhost:5555", false) - - it "returns the matched host", -> - matchesHost("https://shop.apple.com:443/foo", "shop.apple.com") - matchesHost("http://mail.yahoo.com:80/bar", "*yahoo.com") - matchesHost("https://localhost:6666/bar", "localhost:6666") diff --git a/packages/server/test/unit/blacklist_spec.js b/packages/server/test/unit/blacklist_spec.js new file mode 100644 index 000000000000..5c40b2dddd97 --- /dev/null +++ b/packages/server/test/unit/blacklist_spec.js @@ -0,0 +1,53 @@ +require('../spec_helper') + +const blacklist = require(`${root}lib/util/blacklist`) + +const hosts = [ + '*.google.com', + 'shop.apple.com', + 'localhost:6666', + 'adwords.com', + '*yahoo.com', +] + +const matchesStr = function (url, host, val) { + const m = blacklist.matches(url, host) + + expect(!!m).to.eq(val, `url: '${url}' did not pass`) +} + +const matchesArray = function (url, val) { + const m = blacklist.matches(url, hosts) + + expect(!!m).to.eq(val, `url: '${url}' did not pass`) +} + +const matchesHost = (url, host) => { + expect(blacklist.matches(url, hosts)).to.eq(host) +} + +describe('lib/util/blacklist', () => { + it('handles hosts, ports, wildcards', () => { + matchesArray('https://mail.google.com/foo', true) + matchesArray('https://shop.apple.com/bar', true) + matchesArray('http://localhost:6666/', true) + matchesArray('https://localhost:6666/', true) + matchesArray('https://adwords.com:443/', true) + matchesArray('http://adwords.com:80/quux', true) + matchesArray('https://yahoo.com:443/asdf', true) + matchesArray('http://mail.yahoo.com:443/asdf', true) + + matchesArray('https://buy.adwords.com:443/', false) + matchesArray('http://localhost:66667', false) + matchesArray('http://mac.apple.com/', false) + + matchesStr('https://localhost:6666/foo', 'localhost:6666', true) + matchesStr('https://localhost:6666/foo', 'localhost:5555', false) + }) + + it('returns the matched host', () => { + matchesHost('https://shop.apple.com:443/foo', 'shop.apple.com') + matchesHost('http://mail.yahoo.com:80/bar', '*yahoo.com') + matchesHost('https://localhost:6666/bar', 'localhost:6666') + }) +}) diff --git a/packages/server/test/unit/buffers_spec.coffee b/packages/server/test/unit/buffers_spec.coffee deleted file mode 100644 index dad7b1c44c4c..000000000000 --- a/packages/server/test/unit/buffers_spec.coffee +++ /dev/null @@ -1,66 +0,0 @@ -require("../spec_helper") - -buffers = require("#{root}lib/util/buffers") - -describe "lib/util/buffers", -> - beforeEach -> - buffers.reset() - - afterEach -> - buffers.reset() - - context "#get", -> - it "returns buffer by url", -> - obj = {url: "foo"} - - buffers.set(obj) - - buffer = buffers.get("foo") - - expect(buffer).to.deep.eq(obj) - - it "falls back to setting the port when buffer could not be found", -> - obj = {url: "https://www.google.com/"} - - buffers.set(obj) - - buffer = buffers.get("https://www.google.com:443/") - - expect(buffer).to.deep.eq(obj) - - context "#getByOriginalUrl", -> - it "returns buffer by originalUrl", -> - obj = {originalUrl: "foo"} - - buffers.set(obj) - - buffer = buffers.getByOriginalUrl("foo") - - expect(buffer).to.deep.eq(obj) - - context "#take", -> - it "removes the found buffer", -> - obj = {url: "https://www.google.com/"} - - buffers.set(obj) - - expect(buffers.all()).to.have.length(1) - - buffer = buffers.take("https://www.google.com:443/") - - expect(buffer).to.deep.eq(obj) - - expect(buffers.all()).to.have.length(0) - - it "does not remove anything when not found", -> - obj = {url: "https://www.google.com/"} - - buffers.set(obj) - - expect(buffers.all()).to.have.length(1) - - buffer = buffers.take("asdf") - - expect(buffer).to.be.undefined - - expect(buffers.all()).to.have.length(1) diff --git a/packages/server/test/unit/buffers_spec.js b/packages/server/test/unit/buffers_spec.js new file mode 100644 index 000000000000..29885f7c153d --- /dev/null +++ b/packages/server/test/unit/buffers_spec.js @@ -0,0 +1,77 @@ +require('../spec_helper') + +const buffers = require(`${root}lib/util/buffers`) + +describe('lib/util/buffers', () => { + beforeEach(() => { + buffers.reset() + }) + + afterEach(() => { + buffers.reset() + }) + + context('#get', () => { + it('returns buffer by url', () => { + const obj = { url: 'foo' } + + buffers.set(obj) + + const buffer = buffers.get('foo') + + expect(buffer).to.deep.eq(obj) + }) + + it('falls back to setting the port when buffer could not be found', () => { + const obj = { url: 'https://www.google.com/' } + + buffers.set(obj) + + const buffer = buffers.get('https://www.google.com:443/') + + expect(buffer).to.deep.eq(obj) + }) + }) + + context('#getByOriginalUrl', () => { + it('returns buffer by originalUrl', () => { + const obj = { originalUrl: 'foo' } + + buffers.set(obj) + + const buffer = buffers.getByOriginalUrl('foo') + + expect(buffer).to.deep.eq(obj) + }) + }) + + context('#take', () => { + it('removes the found buffer', () => { + const obj = { url: 'https://www.google.com/' } + + buffers.set(obj) + + expect(buffers.all()).to.have.length(1) + + const buffer = buffers.take('https://www.google.com:443/') + + expect(buffer).to.deep.eq(obj) + + expect(buffers.all()).to.have.length(0) + }) + + it('does not remove anything when not found', () => { + const obj = { url: 'https://www.google.com/' } + + buffers.set(obj) + + expect(buffers.all()).to.have.length(1) + + const buffer = buffers.take('asdf') + + expect(buffer).to.be.undefined + + expect(buffers.all()).to.have.length(1) + }) + }) +}) diff --git a/packages/server/test/unit/cors_spec.coffee b/packages/server/test/unit/cors_spec.coffee deleted file mode 100644 index 96ef40eaee7e..000000000000 --- a/packages/server/test/unit/cors_spec.coffee +++ /dev/null @@ -1,214 +0,0 @@ -require("../spec_helper") - -cors = require("#{root}lib/util/cors") - -describe "lib/util/cors", -> - context ".parseUrlIntoDomainTldPort", -> - beforeEach -> - @isEq = (url, obj) -> - expect(cors.parseUrlIntoDomainTldPort(url)).to.deep.eq(obj) - - it "parses https://www.google.com", -> - @isEq("https://www.google.com", { - port: "443" - domain: "google" - tld: "com" - }) - - it "parses http://localhost:8080", -> - @isEq("http://localhost:8080", { - port: "8080" - domain: "" - tld: "localhost" - }) - - it "parses http://app.localhost:8080", -> - @isEq("http://app.localhost:8080", { - port: "8080" - domain: "app" - tld: "localhost" - }) - - it "parses http://app.localhost.dev:8080", -> - @isEq("http://app.localhost.dev:8080", { - port: "8080" - domain: "localhost" - tld: "dev" - }) - - it "parses http://app.local:8080", -> - @isEq("http://app.local:8080", { - port: "8080" - domain: "app" - tld: "local" - }) - - ## public suffix example of a private tld - it "parses https://example.herokuapp.com", -> - @isEq("https://example.herokuapp.com", { - port: "443" - domain: "example" - tld: "herokuapp.com" - }) - - it "parses http://www.local.nl", -> - @isEq("http://www.local.nl", { - port: "80" - domain: "local" - tld: "nl" - }) - - ## https://github.com/cypress-io/cypress/issues/3717 - it "parses http://dev.classea12.beta.gouv.fr", -> - @isEq("http://dev.classea12.beta.gouv.fr", { - port: "80" - domain: "beta" - tld: "gouv.fr" - }) - - it "parses http://www.local.nl:8080", -> - @isEq("http://www.local.nl:8080", { - port: "8080" - domain: "local" - tld: "nl" - }) - - it "parses 192.168.1.1:8080", -> - @isEq("http://192.168.1.1:8080", { - port: "8080" - domain: "" - tld: "192.168.1.1" - }) - - context ".urlMatchesOriginPolicyProps", -> - beforeEach -> - @isFalse = (url, props) => - expect(cors.urlMatchesOriginPolicyProps(url, props)).to.be.false - - @isTrue = (url, props) => - expect(cors.urlMatchesOriginPolicyProps(url, props)).to.be.true - - describe "domain + subdomain", -> - beforeEach -> - @props = cors.parseUrlIntoDomainTldPort("https://staging.google.com") - - it "does not match", -> - @isFalse("https://foo.bar:443", @props) - @isFalse("http://foo.bar:80", @props) - @isFalse("http://foo.bar", @props) - @isFalse("http://staging.google.com", @props) - @isFalse("http://staging.google.com:80", @props) - @isFalse("https://staging.google2.com:443", @props) - @isFalse("https://staging.google.net:443", @props) - @isFalse("https://google.net:443", @props) - @isFalse("http://google.com", @props) - - it "matches", -> - @isTrue("https://staging.google.com:443", @props) - @isTrue("https://google.com:443", @props) - @isTrue("https://foo.google.com:443", @props) - @isTrue("https://foo.bar.google.com:443", @props) - - describe "public suffix", -> - beforeEach -> - @props = cors.parseUrlIntoDomainTldPort("https://example.gitlab.io") - - it "does not match", -> - @isFalse("http://example.gitlab.io", @props) - @isFalse("https://foo.gitlab.io:443", @props) - - it "matches", -> - @isTrue("https://example.gitlab.io:443", @props) - @isTrue("https://foo.example.gitlab.io:443", @props) - - describe "localhost", -> - beforeEach -> - @props = cors.parseUrlIntoDomainTldPort("http://localhost:4200") - - it "does not match", -> - @isFalse("http://localhost:4201", @props) - @isFalse("http://localhoss:4200", @props) - - it "matches", -> - @isTrue("http://localhost:4200", @props) - - describe "app.localhost", -> - beforeEach -> - @props = cors.parseUrlIntoDomainTldPort("http://app.localhost:4200") - - it "does not match", -> - @isFalse("http://app.localhost:4201", @props) - @isFalse("http://app.localhoss:4200", @props) - - it "matches", -> - @isTrue("http://app.localhost:4200", @props) - @isTrue("http://name.app.localhost:4200", @props) - - describe "local", -> - beforeEach -> - @props = cors.parseUrlIntoDomainTldPort("http://brian.dev.local") - - it "does not match", -> - @isFalse("https://brian.dev.local:443", @props) - @isFalse("https://brian.dev.local", @props) - @isFalse("http://brian.dev2.local:81", @props) - - it "matches", -> - @isTrue("http://jennifer.dev.local:80", @props) - @isTrue("http://jennifer.dev.local", @props) - - describe "ip address", -> - beforeEach -> - @props = cors.parseUrlIntoDomainTldPort("http://192.168.5.10") - - it "does not match", -> - @isFalse("http://192.168.5.10:443", @props) - @isFalse("https://192.168.5.10", @props) - @isFalse("http://193.168.5.10", @props) - @isFalse("http://193.168.5.10:80", @props) - - it "matches", -> - @isTrue("http://192.168.5.10", @props) - @isTrue("http://192.168.5.10:80", @props) - - context ".urlMatchesOriginProtectionSpace", -> - isMatch = (urlStr, origin) -> - expect(urlStr, "the url: '#{urlStr}' did not match origin protection space: '#{origin}'").to.satisfy -> - cors.urlMatchesOriginProtectionSpace(urlStr, origin) - - isNotMatch = (urlStr, origin) -> - expect(urlStr, "the url: '#{urlStr}' matched origin protection space: '#{origin}'") - .not.to.satisfy -> - cors.urlMatchesOriginProtectionSpace(urlStr, origin) - - it "ports", -> - isMatch("http://example.com/", "http://example.com:80") - isMatch("http://example.com:80/", "http://example.com") - isMatch("http://example.com:80/", "http://example.com:80") - isMatch("https://example.com:443/", "https://example.com:443") - isMatch("https://example.com:443/", "https://example.com") - isMatch("https://example.com/", "https://example.com:443") - - isNotMatch("https://example.com:1234/", "https://example.com") - isNotMatch("https://example.com:1234/", "https://example.com:443") - - it "schemes", -> - isNotMatch("http://example.com/", "https://example.com") - isNotMatch("https://example.com/", "http://example.com") - isNotMatch("http://example.com/", "ftp://example.com") - isNotMatch("http://example.com/", "file://example.com") - - it "does not factor in path or search", -> - isMatch("http://example.com/foo", "http://example.com") - isMatch("http://example.com/foo/bar", "http://example.com") - isMatch("http://example.com/?foo=bar", "http://example.com") - isMatch("http://example.com/foo?bar=baz", "http://example.com") - - it "subdomains", -> - isMatch("http://example.com/", "http://example.com") - isMatch("http://www.example.com/", "http://www.example.com") - isMatch("http://foo.bar.example.com/", "http://foo.bar.example.com") - - isNotMatch("http://www.example.com/", "http://example.com") - isNotMatch("http://foo.example.com/", "http://bar.example.com") - isNotMatch("http://foo.example.com/", "http://foo.bar.example.com") diff --git a/packages/server/test/unit/cors_spec.js b/packages/server/test/unit/cors_spec.js new file mode 100644 index 000000000000..56b219251020 --- /dev/null +++ b/packages/server/test/unit/cors_spec.js @@ -0,0 +1,265 @@ +require('../spec_helper') + +const cors = require(`${root}lib/util/cors`) + +describe('lib/util/cors', () => { + context('.parseUrlIntoDomainTldPort', () => { + beforeEach(function () { + this.isEq = (url, obj) => { + expect(cors.parseUrlIntoDomainTldPort(url)).to.deep.eq(obj) + } + }) + + it('parses https://www.google.com', function () { + this.isEq('https://www.google.com', { + port: '443', + domain: 'google', + tld: 'com', + }) + }) + + it('parses http://localhost:8080', function () { + this.isEq('http://localhost:8080', { + port: '8080', + domain: '', + tld: 'localhost', + }) + }) + + it('parses http://app.localhost:8080', function () { + this.isEq('http://app.localhost:8080', { + port: '8080', + domain: 'app', + tld: 'localhost', + }) + }) + + it('parses http://app.localhost.dev:8080', function () { + this.isEq('http://app.localhost.dev:8080', { + port: '8080', + domain: 'localhost', + tld: 'dev', + }) + }) + + it('parses http://app.local:8080', function () { + this.isEq('http://app.local:8080', { + port: '8080', + domain: 'app', + tld: 'local', + }) + }) + + // public suffix example of a private tld + it('parses https://example.herokuapp.com', function () { + this.isEq('https://example.herokuapp.com', { + port: '443', + domain: 'example', + tld: 'herokuapp.com', + }) + }) + + it('parses http://www.local.nl', function () { + this.isEq('http://www.local.nl', { + port: '80', + domain: 'local', + tld: 'nl', + }) + }) + + // https://github.com/cypress-io/cypress/issues/3717 + it('parses http://dev.classea12.beta.gouv.fr', function () { + this.isEq('http://dev.classea12.beta.gouv.fr', { + port: '80', + domain: 'beta', + tld: 'gouv.fr', + }) + }) + + it('parses http://www.local.nl:8080', function () { + this.isEq('http://www.local.nl:8080', { + port: '8080', + domain: 'local', + tld: 'nl', + }) + }) + + it('parses 192.168.1.1:8080', function () { + this.isEq('http://192.168.1.1:8080', { + port: '8080', + domain: '', + tld: '192.168.1.1', + }) + }) + }) + + context('.urlMatchesOriginPolicyProps', () => { + beforeEach(function () { + this.isFalse = (url, props) => { + expect(cors.urlMatchesOriginPolicyProps(url, props)).to.be.false + } + + this.isTrue = (url, props) => { + expect(cors.urlMatchesOriginPolicyProps(url, props)).to.be.true + } + }) + + describe('domain + subdomain', () => { + beforeEach(function () { + this.props = cors.parseUrlIntoDomainTldPort('https://staging.google.com') + }) + + it('does not match', function () { + this.isFalse('https://foo.bar:443', this.props) + this.isFalse('http://foo.bar:80', this.props) + this.isFalse('http://foo.bar', this.props) + this.isFalse('http://staging.google.com', this.props) + this.isFalse('http://staging.google.com:80', this.props) + this.isFalse('https://staging.google2.com:443', this.props) + this.isFalse('https://staging.google.net:443', this.props) + this.isFalse('https://google.net:443', this.props) + this.isFalse('http://google.com', this.props) + }) + + it('matches', function () { + this.isTrue('https://staging.google.com:443', this.props) + this.isTrue('https://google.com:443', this.props) + this.isTrue('https://foo.google.com:443', this.props) + this.isTrue('https://foo.bar.google.com:443', this.props) + }) + }) + + describe('public suffix', () => { + beforeEach(function () { + this.props = cors.parseUrlIntoDomainTldPort('https://example.gitlab.io') + }) + + it('does not match', function () { + this.isFalse('http://example.gitlab.io', this.props) + this.isFalse('https://foo.gitlab.io:443', this.props) + }) + + it('matches', function () { + this.isTrue('https://example.gitlab.io:443', this.props) + this.isTrue('https://foo.example.gitlab.io:443', this.props) + }) + }) + + describe('localhost', () => { + beforeEach(function () { + this.props = cors.parseUrlIntoDomainTldPort('http://localhost:4200') + }) + + it('does not match', function () { + this.isFalse('http://localhost:4201', this.props) + this.isFalse('http://localhoss:4200', this.props) + }) + + it('matches', function () { + this.isTrue('http://localhost:4200', this.props) + }) + }) + + describe('app.localhost', () => { + beforeEach(function () { + this.props = cors.parseUrlIntoDomainTldPort('http://app.localhost:4200') + }) + + it('does not match', function () { + this.isFalse('http://app.localhost:4201', this.props) + this.isFalse('http://app.localhoss:4200', this.props) + }) + + it('matches', function () { + this.isTrue('http://app.localhost:4200', this.props) + this.isTrue('http://name.app.localhost:4200', this.props) + }) + }) + + describe('local', () => { + beforeEach(function () { + this.props = cors.parseUrlIntoDomainTldPort('http://brian.dev.local') + }) + + it('does not match', function () { + this.isFalse('https://brian.dev.local:443', this.props) + this.isFalse('https://brian.dev.local', this.props) + this.isFalse('http://brian.dev2.local:81', this.props) + }) + + it('matches', function () { + this.isTrue('http://jennifer.dev.local:80', this.props) + this.isTrue('http://jennifer.dev.local', this.props) + }) + }) + + describe('ip address', () => { + beforeEach(function () { + this.props = cors.parseUrlIntoDomainTldPort('http://192.168.5.10') + }) + + it('does not match', function () { + this.isFalse('http://192.168.5.10:443', this.props) + this.isFalse('https://192.168.5.10', this.props) + this.isFalse('http://193.168.5.10', this.props) + this.isFalse('http://193.168.5.10:80', this.props) + }) + + it('matches', function () { + this.isTrue('http://192.168.5.10', this.props) + this.isTrue('http://192.168.5.10:80', this.props) + }) + }) + }) + + context('.urlMatchesOriginProtectionSpace', () => { + const isMatch = (urlStr, origin) => { + expect(urlStr, `the url: '${urlStr}' did not match origin protection space: '${origin}'`).to.satisfy(() => { + return cors.urlMatchesOriginProtectionSpace(urlStr, origin) + }) + } + + const isNotMatch = (urlStr, origin) => { + expect(urlStr, `the url: '${urlStr}' matched origin protection space: '${origin}'`) + .not.to.satisfy(() => { + return cors.urlMatchesOriginProtectionSpace(urlStr, origin) + }) + } + + it('ports', () => { + isMatch('http://example.com/', 'http://example.com:80') + isMatch('http://example.com:80/', 'http://example.com') + isMatch('http://example.com:80/', 'http://example.com:80') + isMatch('https://example.com:443/', 'https://example.com:443') + isMatch('https://example.com:443/', 'https://example.com') + isMatch('https://example.com/', 'https://example.com:443') + + isNotMatch('https://example.com:1234/', 'https://example.com') + isNotMatch('https://example.com:1234/', 'https://example.com:443') + }) + + it('schemes', () => { + isNotMatch('http://example.com/', 'https://example.com') + isNotMatch('https://example.com/', 'http://example.com') + isNotMatch('http://example.com/', 'ftp://example.com') + isNotMatch('http://example.com/', 'file://example.com') + }) + + it('does not factor in path or search', () => { + isMatch('http://example.com/foo', 'http://example.com') + isMatch('http://example.com/foo/bar', 'http://example.com') + isMatch('http://example.com/?foo=bar', 'http://example.com') + isMatch('http://example.com/foo?bar=baz', 'http://example.com') + }) + + it('subdomains', () => { + isMatch('http://example.com/', 'http://example.com') + isMatch('http://www.example.com/', 'http://www.example.com') + isMatch('http://foo.bar.example.com/', 'http://foo.bar.example.com') + + isNotMatch('http://www.example.com/', 'http://example.com') + isNotMatch('http://foo.example.com/', 'http://bar.example.com') + isNotMatch('http://foo.example.com/', 'http://foo.bar.example.com') + }) + }) +}) diff --git a/packages/server/test/unit/security_spec.coffee b/packages/server/test/unit/security_spec.coffee deleted file mode 100644 index 9eabf0b10dc6..000000000000 --- a/packages/server/test/unit/security_spec.coffee +++ /dev/null @@ -1,277 +0,0 @@ -require("../spec_helper") - -_ = require("lodash") -rp = require("request-promise") -concat = require("concat-stream") -fs = require("#{root}lib/util/fs") -security = require("#{root}lib/util/security") -Fixtures = require("#{root}test/support/helpers/fixtures") - -original = """ - - - top1 - settop - settopbox - parent1 - grandparent - grandparents - topFoo - topFoo.window - topFoo.window != topFoo - parentFoo - parentFoo.window - parentFoo.window != parentFoo - -
-
-
- - parent() - foo.parent() - top() - foo.top() - foo("parent") - foo("top") - - const parent = () => { bar: 'bar' } - - parent.bar - - - - -""" - -expected = """ - - - top1 - settop - settopbox - parent1 - grandparent - grandparents - topFoo - topFoo.window - topFoo.window != topFoo - parentFoo - parentFoo.window - parentFoo.window != parentFoo - -
-
-
- - parent() - foo.parent() - top() - foo.top() - foo("parent") - foo("top") - - const parent = () => { bar: 'bar' } - - parent.bar - - - - -""" - -describe "lib/util/security", -> - context ".strip", -> - it "replaces obstructive code", -> - expect(security.strip(original)).to.eq(expected) - - it "replaces jira window getter", -> - jira = """ - for (; !function (n) { - return n === n.parent - }(n) - """ - - jira2 = """ - function(n){for(;!function(l){return l===l.parent}(l)&&function(l){try{if(void 0==l.location.href)return!1}catch(l){return!1}return!0}(l.parent);)l=l.parent;return l} - """ - - expect(security.strip(jira)).to.eq(""" - for (; !function (n) { - return n === n.parent || n.parent.__Cypress__ - }(n) - """) - - expect(security.strip(jira2)).to.eq(""" - function(n){for(;!function(l){return l===l.parent || l.parent.__Cypress__}(l)&&function(l){try{if(void 0==l.location.href)return!1}catch(l){return!1}return!0}(l.parent);)l=l.parent;return l} - """) - - describe "libs", -> - ## go out and download all of these libs and ensure - ## that we can run them through the security strip - ## and that they are not modified! - - cdnUrl = "https://cdnjs.cloudflare.com/ajax/libs" - - needsDash = ["backbone", "underscore"] - - libs = { - jquery: "#{cdnUrl}/jquery/3.3.1/jquery.js" - jqueryui: "#{cdnUrl}/jqueryui/1.12.1/jquery-ui.js" - angular: "#{cdnUrl}/angular.js/1.6.5/angular.js" - bootstrap: "#{cdnUrl}/twitter-bootstrap/4.0.0/js/bootstrap.js" - fontawesome: "#{cdnUrl}/font-awesome/4.7.0/css/font-awesome.css" - moment: "#{cdnUrl}/moment.js/2.20.1/moment.js" - lodash: "#{cdnUrl}/lodash.js/4.17.5/lodash.js" - vue: "#{cdnUrl}/vue/2.5.13/vue.js" - backbone: "#{cdnUrl}/backbone.js/1.3.3/backbone.js" - cycle: "#{cdnUrl}/cyclejs-core/7.0.0/cycle.js" - d3: "#{cdnUrl}/d3/4.13.0/d3.js" - normalize: "#{cdnUrl}/normalize/8.0.0/normalize.css" - underscore: "#{cdnUrl}/underscore.js/1.8.3/underscore.js" - foundation: "#{cdnUrl}/foundation/6.4.3/js/foundation.js" - require: "#{cdnUrl}/require.js/2.3.5/require.js" - rxjs: "#{cdnUrl}/rxjs/5.5.6/Rx.js" - bluebird: "#{cdnUrl}/bluebird/3.5.1/bluebird.js" - } - - libs = _ - .chain(libs) - .clone() - .reduce (memo, url, lib) -> - memo[lib] = url - memo[lib + "Min"] = url - .replace(/js$/, "min.js") - .replace(/css$/, "min.css") - - if lib in needsDash - memo[lib + "Min"] = url.replace("min", "-min") - - memo - , {} - .extend({ - knockoutDebug: "#{cdnUrl}/knockout/3.4.2/knockout-debug.js" - knockoutMin: "#{cdnUrl}/knockout/3.4.2/knockout-min.js" - emberMin: "#{cdnUrl}/ember.js/2.18.2/ember.min.js" - emberProd: "#{cdnUrl}/ember.js/2.18.2/ember.prod.js" - reactDev: "#{cdnUrl}/react/16.2.0/umd/react.development.js" - reactProd: "#{cdnUrl}/react/16.2.0/umd/react.production.min.js" - vendorBundle: "https://s3.amazonaws.com/internal-test-runner-assets.cypress.io/vendor.bundle.js" - hugeApp: "https://s3.amazonaws.com/internal-test-runner-assets.cypress.io/huge_app.js" - }) - .value() - - _.each libs, (url, lib) -> - it "does not alter code from: '#{lib}'", -> - nock.enableNetConnect() - - @timeout(10000) - - pathToLib = Fixtures.path("server/libs/#{lib}") - - downloadFile = -> - rp(url) - .then (resp) -> - fs - .outputFileAsync(pathToLib, resp) - .return(resp) - fs - .readFileAsync(pathToLib, "utf8") - .catch(downloadFile) - .then (libCode) -> - stripped = security.strip(libCode) - ## nothing should have changed! - - ## TODO: this is currently failing but we're - ## going to accept this for now and make this - ## test pass, but need to refactor to using - ## inline expressions and change the strategy - ## for removing obstructive code - if lib is "hugeApp" - stripped = stripped.replace( - "window.self !== window.self", - "window.self !== window.top" - ) - - try - expect(stripped).to.eq(libCode) - catch err - fs.outputFileSync(pathToLib + "Diff", stripped) - throw new Error("code from '#{lib}' was different") - - context ".stripStream", -> - it "replaces obstructive code", (done) -> - haystacks = original.split("\n") - - replacer = security.stripStream() - - replacer.pipe concat {encoding: "string"}, (str) -> - str = str.trim() - - try - expect(str).to.eq(expected) - done() - catch err - done(err) - - haystacks.forEach (haystack) -> - replacer.write(haystack + "\n") - - replacer.end() diff --git a/packages/server/test/unit/security_spec.js b/packages/server/test/unit/security_spec.js new file mode 100644 index 000000000000..3f0dfdea37e0 --- /dev/null +++ b/packages/server/test/unit/security_spec.js @@ -0,0 +1,298 @@ +require('../spec_helper') + +const _ = require('lodash') +const rp = require('request-promise') +const concat = require('concat-stream') +const fs = require(`${root}lib/util/fs`) +const security = require(`${root}lib/util/security`) +const Fixtures = require(`${root}test/support/helpers/fixtures`) + +const original = `\ + + + top1 + settop + settopbox + parent1 + grandparent + grandparents + topFoo + topFoo.window + topFoo.window != topFoo + parentFoo + parentFoo.window + parentFoo.window != parentFoo + +
+
+
+ + parent() + foo.parent() + top() + foo.top() + foo("parent") + foo("top") + + const parent = () => { bar: 'bar' } + + parent.bar + + + +\ +` + +const expected = `\ + + + top1 + settop + settopbox + parent1 + grandparent + grandparents + topFoo + topFoo.window + topFoo.window != topFoo + parentFoo + parentFoo.window + parentFoo.window != parentFoo + +
+
+
+ + parent() + foo.parent() + top() + foo.top() + foo("parent") + foo("top") + + const parent = () => { bar: 'bar' } + + parent.bar + + + +\ +` + +describe('lib/util/security', () => { + context('.strip', () => { + it('replaces obstructive code', () => { + expect(security.strip(original)).to.eq(expected) + }) + + it('replaces jira window getter', () => { + const jira = `\ +for (; !function (n) { + return n === n.parent +}(n)\ +` + + const jira2 = `\ +function(n){for(;!function(l){return l===l.parent}(l)&&function(l){try{if(void 0==l.location.href)return!1}catch(l){return!1}return!0}(l.parent);)l=l.parent;return l}\ +` + + expect(security.strip(jira)).to.eq(`\ +for (; !function (n) { + return n === n.parent || n.parent.__Cypress__ +}(n)\ +`) + + expect(security.strip(jira2)).to.eq(`\ +function(n){for(;!function(l){return l===l.parent || l.parent.__Cypress__}(l)&&function(l){try{if(void 0==l.location.href)return!1}catch(l){return!1}return!0}(l.parent);)l=l.parent;return l}\ +`) + }) + + describe('libs', () => { + // go out and download all of these libs and ensure + // that we can run them through the security strip + // and that they are not modified! + + const cdnUrl = 'https://cdnjs.cloudflare.com/ajax/libs' + + const needsDash = ['backbone', 'underscore'] + + let libs = { + jquery: `${cdnUrl}/jquery/3.3.1/jquery.js`, + jqueryui: `${cdnUrl}/jqueryui/1.12.1/jquery-ui.js`, + angular: `${cdnUrl}/angular.js/1.6.5/angular.js`, + bootstrap: `${cdnUrl}/twitter-bootstrap/4.0.0/js/bootstrap.js`, + fontawesome: `${cdnUrl}/font-awesome/4.7.0/css/font-awesome.css`, + moment: `${cdnUrl}/moment.js/2.20.1/moment.js`, + lodash: `${cdnUrl}/lodash.js/4.17.5/lodash.js`, + vue: `${cdnUrl}/vue/2.5.13/vue.js`, + backbone: `${cdnUrl}/backbone.js/1.3.3/backbone.js`, + cycle: `${cdnUrl}/cyclejs-core/7.0.0/cycle.js`, + d3: `${cdnUrl}/d3/4.13.0/d3.js`, + normalize: `${cdnUrl}/normalize/8.0.0/normalize.css`, + underscore: `${cdnUrl}/underscore.js/1.8.3/underscore.js`, + foundation: `${cdnUrl}/foundation/6.4.3/js/foundation.js`, + require: `${cdnUrl}/require.js/2.3.5/require.js`, + rxjs: `${cdnUrl}/rxjs/5.5.6/Rx.js`, + bluebird: `${cdnUrl}/bluebird/3.5.1/bluebird.js`, + } + + libs = _ + .chain(libs) + .clone() + .reduce((memo, url, lib) => { + memo[lib] = url + memo[`${lib}Min`] = url + .replace(/js$/, 'min.js') + .replace(/css$/, 'min.css') + + if (needsDash.includes(lib)) { + memo[`${lib}Min`] = url.replace('min', '-min') + } + + return memo + } + , {}) + .extend({ + knockoutDebug: `${cdnUrl}/knockout/3.4.2/knockout-debug.js`, + knockoutMin: `${cdnUrl}/knockout/3.4.2/knockout-min.js`, + emberMin: `${cdnUrl}/ember.js/2.18.2/ember.min.js`, + emberProd: `${cdnUrl}/ember.js/2.18.2/ember.prod.js`, + reactDev: `${cdnUrl}/react/16.2.0/umd/react.development.js`, + reactProd: `${cdnUrl}/react/16.2.0/umd/react.production.min.js`, + vendorBundle: 'https://s3.amazonaws.com/internal-test-runner-assets.cypress.io/vendor.bundle.js', + hugeApp: 'https://s3.amazonaws.com/internal-test-runner-assets.cypress.io/huge_app.js', + }) + .value() + + return _.each(libs, (url, lib) => { + it(`does not alter code from: '${lib}'`, function () { + nock.enableNetConnect() + + this.timeout(10000) + + const pathToLib = Fixtures.path(`server/libs/${lib}`) + + const downloadFile = () => { + return rp(url) + .then((resp) => { + return fs + .outputFileAsync(pathToLib, resp) + .return(resp) + }) + } + + return fs + .readFileAsync(pathToLib, 'utf8') + .catch(downloadFile) + .then((libCode) => { + let stripped = security.strip(libCode) + // nothing should have changed! + + // TODO: this is currently failing but we're + // going to accept this for now and make this + // test pass, but need to refactor to using + // inline expressions and change the strategy + // for removing obstructive code + if (lib === 'hugeApp') { + stripped = stripped.replace( + 'window.self !== window.self', + 'window.self !== window.top' + ) + } + + try { + expect(stripped).to.eq(libCode) + } catch (err) { + fs.outputFileSync(`${pathToLib}Diff`, stripped) + throw new Error(`code from '${lib}' was different`) + } + }) + }) + }) + }) + }) + + context('.stripStream', () => { + it('replaces obstructive code', (done) => { + const haystacks = original.split('\n') + + const replacer = security.stripStream() + + replacer.pipe(concat({ encoding: 'string' }, (str) => { + str = str.trim() + + try { + expect(str).to.eq(expected) + + done() + } catch (err) { + done(err) + } + })) + + haystacks.forEach((haystack) => { + replacer.write(`${haystack}\n`) + }) + + replacer.end() + }) + }) +})