tor push
This commit is contained in:
+166
@@ -0,0 +1,166 @@
|
||||
const PageObject = require('./page-objects/PageObject.js')
|
||||
const OptionsPageObject = require('./page-objects/OptionsPageObject.js')
|
||||
|
||||
const path = require('path')
|
||||
const assert = require('chai')
|
||||
const expect = assert.expect
|
||||
|
||||
const webExtensionsGeckoDriver = require('webextensions-geckodriver')
|
||||
const { webdriver, firefox } = webExtensionsGeckoDriver
|
||||
const { until, By } = webdriver
|
||||
|
||||
const manifestPath = path.resolve(path.join(__dirname, '../../dist/manifest.json'))
|
||||
|
||||
describe('Container Proxy extension', function () {
|
||||
let geckodriver
|
||||
this.timeout(30000)
|
||||
|
||||
before(async () => {
|
||||
const fxOptions = new firefox.Options()
|
||||
if (process.env.HEADLESS) {
|
||||
fxOptions.headless()
|
||||
.windowSize({ height: 1080, width: 1920 })
|
||||
}
|
||||
|
||||
const webExtension = await webExtensionsGeckoDriver(manifestPath, { fxOptions })
|
||||
geckodriver = webExtension.geckodriver
|
||||
})
|
||||
|
||||
it('should add a proxy', async () => {
|
||||
const helper = new Helper(geckodriver)
|
||||
|
||||
const options = await helper.openOptionsPage()
|
||||
|
||||
let proxyList = await options.openProxyList()
|
||||
|
||||
const proxyForm = await proxyList.openAddProxyForm()
|
||||
|
||||
await proxyForm.selectProtocol('socks')
|
||||
await proxyForm.typeInServer('localhost')
|
||||
await proxyForm.typeInPort(1080)
|
||||
await proxyForm.typeInUsername('user')
|
||||
await proxyForm.typeInPassword('password')
|
||||
|
||||
await proxyForm.testSettings()
|
||||
|
||||
proxyList = await proxyForm.saveSettings()
|
||||
|
||||
const proxyLabel = 'socks://localhost:1080'
|
||||
await geckodriver.wait(async () => {
|
||||
const row = await geckodriver.wait(until.elementLocated(
|
||||
By.css('.proxy-list-item:first-of-type')
|
||||
), 2000)
|
||||
|
||||
const label = row.findElement(By.css('.proxy-name'))
|
||||
|
||||
const text = await label.getText()
|
||||
return text === proxyLabel
|
||||
}, 1000, 'Should show proxy in the list')
|
||||
|
||||
const assign = await options.openAssignProxy()
|
||||
const defaultContainerSelect = await assign.defaultContainerSelect()
|
||||
await defaultContainerSelect.selectByLabel(proxyLabel)
|
||||
})
|
||||
|
||||
it.skip('should contain IP address text', async () => {
|
||||
await geckodriver.setContext(firefox.Context.CONTENT)
|
||||
await geckodriver.get('https://api.duckduckgo.com/?q=ip&no_html=1&format=json&t=firefox-container-proxy-extension')
|
||||
const text = await geckodriver.getPageSource()
|
||||
|
||||
expect(text).to.include('Your IP address is')
|
||||
})
|
||||
|
||||
it('should successfully use SOCKS5 proxy for default container', async () => {
|
||||
const helper = new Helper(geckodriver)
|
||||
|
||||
const optionsPage = await helper.openOptionsPage()
|
||||
const proxyList = await optionsPage.openProxyList()
|
||||
const addProxyForm = await proxyList.openAddProxyForm()
|
||||
const title = 'Valid SOCKS5 proxy'
|
||||
await addProxyForm.addProxy({
|
||||
title: title,
|
||||
type: 'socks',
|
||||
server: 'localhost',
|
||||
port: 1080,
|
||||
username: 'user',
|
||||
password: 'password'
|
||||
})
|
||||
// TODO: Check if username and password are actually verified by "dante"
|
||||
|
||||
const assignProxy = await optionsPage.openAssignProxy()
|
||||
await assignProxy.selectForDefaultContainer(title)
|
||||
await helper.assertCanGetTheIpAddress()
|
||||
})
|
||||
|
||||
it('should fail with incorrect SOCKS5 proxy settings', async () => {
|
||||
const helper = new Helper(geckodriver)
|
||||
|
||||
const optionsPage = await helper.openOptionsPage()
|
||||
const proxyList = await optionsPage.openProxyList()
|
||||
const addProxyForm = await proxyList.openAddProxyForm()
|
||||
const title = 'Incorrectly setup SOCKS5 proxy'
|
||||
await addProxyForm.addProxy({
|
||||
title: title,
|
||||
type: 'socks',
|
||||
server: 'localhost',
|
||||
port: 999,
|
||||
username: 'user',
|
||||
password: 'password'
|
||||
})
|
||||
|
||||
const assignProxy = await optionsPage.openAssignProxy()
|
||||
await assignProxy.selectForDefaultContainer(title)
|
||||
await helper.assertProxyFailure()
|
||||
})
|
||||
|
||||
after(function () {
|
||||
geckodriver.quit()
|
||||
})
|
||||
})
|
||||
|
||||
class Helper extends PageObject {
|
||||
toolbarButton = By.id('container-proxy_bekh-ivanov_me-browser-action')
|
||||
|
||||
/**
|
||||
* @return {Promise<OptionsPageObject>}
|
||||
*/
|
||||
async openOptionsPage() {
|
||||
await this._driver.setContext(firefox.Context.CHROME)
|
||||
await this.click(this.toolbarButton)
|
||||
await this._driver.setContext(firefox.Context.CONTENT)
|
||||
|
||||
let windowHandle
|
||||
await this._driver.wait(async () => {
|
||||
const windowHandles = await this._driver.getAllWindowHandles()
|
||||
windowHandle = windowHandles[windowHandles.length - 1]
|
||||
try {
|
||||
await this._driver.switchTo().window(windowHandle)
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
const title = await this._driver.getTitle()
|
||||
return title === 'Container Proxy extension settings'
|
||||
}, 2000, 'Should have opened Container Proxy extension settings')
|
||||
|
||||
return this.createPageObject(OptionsPageObject)
|
||||
}
|
||||
|
||||
async assertCanGetTheIpAddress() {
|
||||
await this._driver.setContext(firefox.Context.CONTENT)
|
||||
await this._driver.get('https://duckduckgo.com/?q=ip&ia=answer&atb=v150-1')
|
||||
const text = await this._driver.getPageSource()
|
||||
|
||||
expect(text).to.include('Your IP address is')
|
||||
}
|
||||
|
||||
async assertProxyFailure() {
|
||||
await this._driver.setContext(firefox.Context.CONTENT)
|
||||
try {
|
||||
await this._driver.get('https://api.duckduckgo.com/?q=ip&no_html=1&format=json&t=firefox-container-proxy-extension')
|
||||
} catch (e) {
|
||||
}
|
||||
const text = await this._driver.getPageSource()
|
||||
|
||||
expect(text).to.include('Firefox is configured to use a proxy server that is refusing connections.')
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
version: "3"
|
||||
services:
|
||||
socks:
|
||||
build: ./socks
|
||||
ports:
|
||||
- "1080:1080"
|
||||
http:
|
||||
build: ./http
|
||||
ports:
|
||||
- "3128:3128"
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
FROM datadog/squid
|
||||
|
||||
RUN apt update && apt install -y apache2-utils
|
||||
|
||||
RUN htpasswd -b -c /etc/squid/passwords userhttp passwordhttp
|
||||
|
||||
ADD squid.conf /etc/squid/squid.conf
|
||||
|
||||
RUN chmod o+rw /var/run
|
||||
|
||||
USER proxy
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
auth_param basic program /usr/lib/squid3/basic_ncsa_auth /etc/squid/passwords
|
||||
#auth_param basic children 5 startup = 5 idle = 1
|
||||
auth_param basic realm proxy
|
||||
auth_param basic credentialsttl 1 second
|
||||
connect_timeout 1 second
|
||||
acl authenticated proxy_auth REQUIRED
|
||||
http_access allow authenticated
|
||||
|
||||
logformat splunk_recommended_squid %ts.%03tu logformat=splunk_recommended_squid duration=%tr src_ip=%>a src_port=%>p dest_ip=%<a dest_port=%<p user_ident="%[ui" user="%[un" local_time=[%tl] http_method=%rm request_method_from_client=%<rm request_method_to_server=%>rm url="%ru" http_referrer="%{Referer}>h" http_user_agent="%{User-Agent}>h" status=%>Hs vendor_action=%Ss dest_status=%Sh total_time_milliseconds=%<tt http_content_type="%mt" bytes=%st bytes_in=%>st bytes_out=%<st sni=ssl::>sni
|
||||
|
||||
logfile_rotate 0
|
||||
cache_log stdio:/proc/self/fd/1 splunk_recommended_squid
|
||||
access_log stdio:/proc/self/fd/1 splunk_recommended_squid
|
||||
cache_store_log stdio:/proc/self/fd/1 splunk_recommended_squid
|
||||
|
||||
# Choose the port you want. Below we set it to default 3128.
|
||||
http_port 3128
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
|
||||
|
||||
|
||||
user:$apr1$L24dGKxY$xqj/npbeiBkMZEH7s9Iae/
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
FROM vimagick/dante
|
||||
|
||||
RUN useradd user && echo user:password | chpasswd
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
import BackgroundMain, { doNotProxy } from '../../src/background/BackgroundMain'
|
||||
import { Store } from '../../src/store/Store'
|
||||
import webExtensionsApiFake from 'webextensions-api-fake'
|
||||
|
||||
import { expect } from 'chai'
|
||||
import { ProxySettings } from '../../src/domain/ProxySettings'
|
||||
const tryFromDao = ProxySettings.tryFromDao
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
||||
|
||||
const store = new Store()
|
||||
|
||||
describe('BackgroundMain', function () {
|
||||
beforeEach(() => {
|
||||
// @ts-expect-error
|
||||
global.browser = webExtensionsApiFake()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
// @ts-expect-error
|
||||
delete global.browser
|
||||
})
|
||||
|
||||
const backgroundMain = new BackgroundMain({ store: store })
|
||||
|
||||
// TODO: Add test for proxyDNS property
|
||||
|
||||
describe('onRequest', function () {
|
||||
it('should return empty array if no proxy is set up', async () => {
|
||||
const result = await backgroundMain.onRequest({ cookieStoreId: 'firefox-default', url: 'https://google.com', tabId: 0 })
|
||||
|
||||
expect(result).to.be.deep.equal(doNotProxy)
|
||||
})
|
||||
|
||||
it('should return proxy if proxy is set up', async () => {
|
||||
await givenSomeProxyIsSetUpForContainer({ containerId: 'firefox-default', host: undefined, doNotProxyLocal: undefined })
|
||||
|
||||
const result = await backgroundMain.onRequest({ cookieStoreId: 'firefox-default', url: 'https://google.com', tabId: 0 })
|
||||
|
||||
expect(result).to.be.an('array')
|
||||
expect(result).to.be.not.empty
|
||||
})
|
||||
|
||||
it('should remove doNotProxyLocal flag from proxy settings if proxy is set up', async () => {
|
||||
await givenSomeProxyIsSetUpForContainer({ containerId: 'firefox-default', host: undefined, doNotProxyLocal: undefined })
|
||||
|
||||
const result = await backgroundMain.onRequest({ cookieStoreId: 'firefox-default', url: 'https://google.com', tabId: 0 })
|
||||
|
||||
expect((result[0] as any).doNotProxyLocal).to.be.undefined
|
||||
})
|
||||
|
||||
it('should return proxy for the container if url is invalid', async () => {
|
||||
// To be more on a safe side
|
||||
await givenSomeProxyIsSetUpForContainer({ containerId: 'firefox-default', host: undefined, doNotProxyLocal: undefined })
|
||||
|
||||
const result = await backgroundMain.onRequest({ cookieStoreId: 'firefox-default', url: 'np-protocol-url.com', tabId: 0 })
|
||||
|
||||
expect(result).to.be.an('array')
|
||||
expect(result).to.be.not.empty
|
||||
})
|
||||
|
||||
// Connections to localhost, 127.0.0.1, and ::1 are never proxied. (From FF settings)
|
||||
const localAddresses = [
|
||||
'http://localhost/index.html',
|
||||
'https://localhost/index.html',
|
||||
'http://127.0.0.1/',
|
||||
'https://127.0.0.1/',
|
||||
'http://[::1]/test',
|
||||
'https://[::1]/test',
|
||||
'http://[0:0:0:0:0:0:0:1]/test',
|
||||
'https://[0:0:0:0:0:0:0:1]/test',
|
||||
'https://user:password@127.0.0.1:123/',
|
||||
'http://[::1]:123/test'
|
||||
]
|
||||
|
||||
describe('proxying of local addresses is disabled', () => {
|
||||
localAddresses.forEach(url => {
|
||||
it(`should return empty array if the address is local: ${url}`, async () => {
|
||||
await givenSomeProxyIsSetUpForContainer({ containerId: 'container1', host: undefined, doNotProxyLocal: true })
|
||||
|
||||
const result = await backgroundMain.onRequest({ cookieStoreId: 'container1', url, tabId: 0 })
|
||||
|
||||
expect(result).to.be.deep.equal(doNotProxy)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('proxying of local addresses is enabled', () => {
|
||||
localAddresses.forEach(url => {
|
||||
it(`should return array with proxy: ${url}`, async () => {
|
||||
const host = 'proxyX.example.com'
|
||||
await givenSomeProxyIsSetUpForContainer({ host, containerId: 'container1', doNotProxyLocal: false })
|
||||
|
||||
const result = await backgroundMain.onRequest({ cookieStoreId: 'container1', url, tabId: 0 })
|
||||
|
||||
expect(result[0].host).to.be.equal(host)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
async function givenSomeProxyIsSetUpForContainer({ host, containerId, doNotProxyLocal }: any): Promise<void> {
|
||||
const proxyId = 'proxy1'
|
||||
const proxy: any = {
|
||||
id: proxyId,
|
||||
type: 'socks',
|
||||
host: (host as string) ?? 'example.com',
|
||||
port: 1080
|
||||
}
|
||||
if (typeof doNotProxyLocal !== 'undefined') {
|
||||
proxy.doNotProxyLocal = doNotProxyLocal
|
||||
}
|
||||
await store.putProxy(tryFromDao(proxy) as ProxySettings)
|
||||
|
||||
await store.setContainerProxyRelation(containerId, proxyId)
|
||||
}
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
import { ProxyDao, Store } from '../../src/store/Store'
|
||||
import webExtensionsApiFake from 'webextensions-api-fake'
|
||||
|
||||
import { expect } from 'chai'
|
||||
import { ProxySettings } from '../../src/domain/ProxySettings'
|
||||
const tryFromDao = ProxySettings.tryFromDao
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
||||
|
||||
describe('Store', () => {
|
||||
const store = new Store()
|
||||
|
||||
beforeEach(() => {
|
||||
(global as any).browser = webExtensionsApiFake()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
delete (global as any).browser
|
||||
})
|
||||
|
||||
function someProxyWith(id: string, props: Partial<ProxyDao> = {}): ProxySettings {
|
||||
const dao = {
|
||||
id: id,
|
||||
title: 'some title',
|
||||
type: 'socks',
|
||||
host: 'localhost',
|
||||
port: 1080,
|
||||
username: 'user',
|
||||
password: 'password',
|
||||
proxyDNS: true,
|
||||
failoverTimeout: 5,
|
||||
doNotProxyLocal: true
|
||||
}
|
||||
return tryFromDao({ ...dao, ...props }) as ProxySettings
|
||||
}
|
||||
|
||||
describe('getAllProxies', function () {
|
||||
it('should return empty array if nothing is saved', async () => {
|
||||
const proxies = await store.getAllProxies()
|
||||
|
||||
expect(proxies).to.be.an('array')
|
||||
expect(proxies).to.be.empty
|
||||
})
|
||||
})
|
||||
|
||||
it('should put and get the proxy back', async () => {
|
||||
const id = 'someId'
|
||||
const proxy = someProxyWith(id)
|
||||
|
||||
await store.putProxy(proxy)
|
||||
|
||||
const gotProxy = await store.getProxyById(id)
|
||||
|
||||
expect(gotProxy).to.be.deep.equal(proxy)
|
||||
})
|
||||
|
||||
it('when get, returns doNotProxyLocal `true` if was not set when put', async () => {
|
||||
const id = 'someId'
|
||||
const proxy = someProxyWith(id)
|
||||
delete (proxy as any).doNotProxyLocal
|
||||
|
||||
await store.putProxy(proxy)
|
||||
|
||||
const gotProxy = await store.getProxyById(id)
|
||||
|
||||
expect(gotProxy?.doNotProxyLocal).to.be.equal(true)
|
||||
})
|
||||
|
||||
it('stores doNotProxyLocal value', async () => {
|
||||
const id = 'someId'
|
||||
const proxy = someProxyWith(id, { doNotProxyLocal: false })
|
||||
|
||||
await store.putProxy(proxy)
|
||||
|
||||
const gotProxy = await store.getProxyById(id)
|
||||
|
||||
expect(gotProxy?.doNotProxyLocal).to.be.equal(false)
|
||||
})
|
||||
|
||||
it('should be able to delete proxy', async () => {
|
||||
const id = 'someId'
|
||||
await store.putProxy(someProxyWith(id))
|
||||
await store.deleteProxyById(id)
|
||||
|
||||
const result = await store.getProxyById(id)
|
||||
|
||||
expect(result).to.be.null
|
||||
})
|
||||
|
||||
// describe('getProxiesForContainer', () => {
|
||||
// it('should find proxy by container id if one present', async () => {
|
||||
// const cookieStoreId = 'container-1'
|
||||
// const proxyId = 'proxy-1'
|
||||
// // TODO Store should probably take care of this
|
||||
// const relations = {
|
||||
// [cookieStoreId]: [proxyId]
|
||||
// }
|
||||
// const givenProxy = someProxyWith(proxyId)
|
||||
// await store.putProxy(givenProxy)
|
||||
|
||||
// await browser.storage.local.set({ relations: relations })
|
||||
|
||||
// const [gotProxy] = await store.getProxiesForContainer(cookieStoreId)
|
||||
|
||||
// expect(gotProxy).to.be.deep.equal(givenProxy)
|
||||
// })
|
||||
|
||||
// it('doNotProxyLocal should be `true` if not set', async () => {
|
||||
// const cookieStoreId = 'container-1'
|
||||
// const proxyId = 'proxy-1'
|
||||
// // TODO Store should probably take care of this
|
||||
// const relations = {
|
||||
// [cookieStoreId]: [proxyId]
|
||||
// }
|
||||
// const givenProxy = someProxyWith(proxyId)
|
||||
// delete (givenProxy as any).doNotProxyLocal
|
||||
// await store.putProxy(givenProxy)
|
||||
|
||||
// await browser.storage.local.set({ relations: relations })
|
||||
|
||||
// const [gotProxy] = await store.getProxiesForContainer(cookieStoreId)
|
||||
|
||||
// expect(gotProxy.doNotProxyLocal).to.be.equal(true)
|
||||
// })
|
||||
|
||||
// it('should return empty array if proxy not set for the container', async () => {
|
||||
// const cookieStoreId = 'container-1'
|
||||
|
||||
// const result = await store.getProxiesForContainer(cookieStoreId)
|
||||
|
||||
// expect(result).to.be.empty
|
||||
// })
|
||||
|
||||
// it('should return empty array if proxy is set for the container but does not exist', async () => {
|
||||
// const cookieStoreId = 'container-1'
|
||||
// const proxyId = 'something-absent'
|
||||
// const relations = {
|
||||
// [cookieStoreId]: [proxyId]
|
||||
// }
|
||||
// await browser.storage.local.set({ relations: relations })
|
||||
|
||||
// const result = await store.getProxiesForContainer(cookieStoreId)
|
||||
|
||||
// expect(result).to.be.empty
|
||||
// })
|
||||
// })
|
||||
|
||||
describe('setContainerProxyRelation', function () {
|
||||
it('should set the relation', async () => {
|
||||
await store.setContainerProxyRelation('container1', 'proxy1')
|
||||
|
||||
const relations = await store.getRelations()
|
||||
|
||||
expect(relations.container1).to.be.deep.equal(['proxy1'])
|
||||
})
|
||||
|
||||
it('should not remove existing relations', async () => {
|
||||
await store.setContainerProxyRelation('container1', 'proxy1')
|
||||
await store.setContainerProxyRelation('container2', 'proxy2')
|
||||
|
||||
const relations = await store.getRelations()
|
||||
|
||||
expect(relations.container1).to.be.deep.equal(['proxy1'])
|
||||
expect(relations.container2).to.be.deep.equal(['proxy2'])
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user