Unverified Commit cfd5e78e authored by David Disu's avatar David Disu Committed by GitHub

Merge pull request #134 from ethereum/fix-origin

Include supported origins & Fix trailing slash bug
parents 4da76d59 051808fb
{
"data":[
"http://remix-alpha.ethereum.org",
"http://remix.ethereum.org",
"https://remix-alpha.ethereum.org",
"https://remix.ethereum.org",
"package://a7df6d3c223593f3550b35e90d7b0b1f.mod",
"package://6fd22d6fe5549ad4c4d8fd3ca0b7816b.mod",
"https://ipfsgw.komputing.org"
]
}
\ No newline at end of file
...@@ -2,54 +2,96 @@ ...@@ -2,54 +2,96 @@
import WebSocket from '../src/websocket' import WebSocket from '../src/websocket'
import * as servicesList from '../src/serviceList' import * as servicesList from '../src/serviceList'
import { WS } from '../types/index' import { WS } from '../types/index'
import { getDomain } from '../src/utils'
import Axios from 'axios'
import * as fs from 'fs-extra'
import * as path from 'path'
const program = require('commander') (async () => {
const program = require('commander')
program
.usage('-s <shared folder>')
.description('Provide a two-way connection between the local computer and Remix IDE')
.option('--remix-ide <url>', 'URL of remix instance allowed to connect to this web sockect connection')
.option('-s, --shared-folder <path>', 'Folder to share with Remix IDE')
.option('--read-only', 'Treat shared folder as read-only (experimental)')
.on('--help', function(){
console.log('\nExample:\n\n remixd -s ./ --remix-ide http://localhost:8080')
}).parse(process.argv)
program const killCallBack: Array<Function> = []
.usage('-s <shared folder>')
.description('Provide a two-way connection between the local computer and Remix IDE')
.option('--remix-ide <url>', 'URL of remix instance allowed to connect to this web sockect connection')
.option('-s, --shared-folder <path>', 'Folder to share with Remix IDE')
.option('--read-only', 'Treat shared folder as read-only (experimental)')
.on('--help', function(){
console.log('\nExample:\n\n remixd -s ./ --remix-ide http://localhost:8080')
}).parse(process.argv)
const killCallBack: Array<Function> = [] if (!program.remixIde) {
console.log('\x1b[33m%s\x1b[0m', '[WARN] You can only connect to remixd from one of the supported origins.')
} else {
const isValid = await isValidOrigin(program.remixIde)
/* Allow unsupported origins and display warning. */
if (!isValid) {
console.log('\x1b[33m%s\x1b[0m', '[WARN] You are using IDE from an unsupported origin.')
console.log('\x1b[33m%s\x1b[0m', 'Check https://gist.github.com/EthereumRemix/091ccc57986452bbb33f57abfb13d173 for list of all supported origins.\n')
// return
}
console.log('\x1b[33m%s\x1b[0m', '[WARN] You may now only use IDE at ' + program.remixIde + ' to connect to that instance')
}
if (!program.remixIde) { if (program.sharedFolder) {
console.log('\x1b[31m%s\x1b[0m', '[ERR] URL Remix IDE instance has to be provided.') console.log('\x1b[33m%s\x1b[0m', '[WARN] Any application that runs on your computer can potentially read from and write to all files in the directory.')
} console.log('\x1b[33m%s\x1b[0m', '[WARN] Symbolic links are not forwarded to Remix IDE\n')
console.log('\x1b[33m%s\x1b[0m', '[WARN] You may now only use IDE at ' + program.remixIde + ' to connect to that instance') try {
const sharedFolderClient = new servicesList['sharedfolder']()
const websocketHandler = new WebSocket(65520, { remixIdeUrl: program.remixIde }, sharedFolderClient)
if (program.sharedFolder) { websocketHandler.start((ws: WS) => {
console.log('\x1b[33m%s\x1b[0m', '[WARN] Any application that runs on your computer can potentially read from and write to all files in the directory.') sharedFolderClient.setWebSocket(ws)
console.log('\x1b[33m%s\x1b[0m', '[WARN] Symbolic links are not forwarded to Remix IDE\n') sharedFolderClient.setupNotifications(program.sharedFolder)
try { sharedFolderClient.sharedFolder(program.sharedFolder, program.readOnly || false)
const sharedFolderClient = new servicesList['sharedfolder']() })
const websocketHandler = new WebSocket(65520, { remixIdeUrl: program.remixIde }, sharedFolderClient) killCallBack.push(websocketHandler.close.bind(websocketHandler))
} catch(error) {
throw new Error(error)
}
}
websocketHandler.start((ws: WS) => { // kill
sharedFolderClient.setWebSocket(ws) function kill () {
sharedFolderClient.setupNotifications(program.sharedFolder) for (const k in killCallBack) {
sharedFolderClient.sharedFolder(program.sharedFolder, program.readOnly || false) try {
}) killCallBack[k]()
killCallBack.push(websocketHandler.close.bind(websocketHandler)) } catch (e) {
} catch(error) { console.log(e)
throw new Error(error) }
}
} }
}
process.on('SIGINT', kill) // catch ctrl-c
process.on('SIGTERM', kill) // catch kill
process.on('exit', kill)
// kill async function isValidOrigin (origin: string): Promise<any> {
function kill () { if (!origin) return false
for (const k in killCallBack) { const domain = getDomain(origin)
const gistUrl = 'https://gist.githubusercontent.com/EthereumRemix/091ccc57986452bbb33f57abfb13d173/raw/3367e019335746b73288e3710af2922d4c8ef5a3/origins.json'
try { try {
killCallBack[k]() const { data } = await Axios.get(gistUrl)
try {
await fs.writeJSON(path.resolve(__dirname + '/origins.json'), { data })
} catch (e) {
console.error(e)
}
return data.includes(origin) ? data.includes(origin) : data.includes(domain)
} catch (e) { } catch (e) {
console.log(e) try {
const origins = require('./origins.json')
const { data } = origins
return data.includes(origin) ? data.includes(origin) : data.includes(domain)
} catch (e) {
return false
}
} }
} }
} })()
process.on('SIGINT', kill) // catch ctrl-c
process.on('SIGTERM', kill) // catch kill
process.on('exit', kill)
...@@ -110,6 +110,15 @@ ...@@ -110,6 +110,15 @@
"defer-to-connect": "^1.0.1" "defer-to-connect": "^1.0.1"
} }
}, },
"@types/axios": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz",
"integrity": "sha1-7CMA++fX3d1+udOr+HmZlkyvzkY=",
"dev": true,
"requires": {
"axios": "*"
}
},
"@types/color-name": { "@types/color-name": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
...@@ -449,6 +458,14 @@ ...@@ -449,6 +458,14 @@
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
}, },
"axios": {
"version": "0.20.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.20.0.tgz",
"integrity": "sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA==",
"requires": {
"follow-redirects": "^1.10.0"
}
},
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
...@@ -1584,6 +1601,11 @@ ...@@ -1584,6 +1601,11 @@
"integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
"dev": true "dev": true
}, },
"follow-redirects": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
"integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA=="
},
"for-in": { "for-in": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
"@remixproject/plugin-api": "0.3.0-beta.5", "@remixproject/plugin-api": "0.3.0-beta.5",
"@remixproject/plugin-utils": "0.3.0-beta.5", "@remixproject/plugin-utils": "0.3.0-beta.5",
"@remixproject/plugin-ws": "^0.3.0-beta.8", "@remixproject/plugin-ws": "^0.3.0-beta.8",
"axios": "^0.20.0",
"chokidar": "^2.1.8", "chokidar": "^2.1.8",
"commander": "^2.20.3", "commander": "^2.20.3",
"fs-extra": "^3.0.1", "fs-extra": "^3.0.1",
...@@ -49,6 +50,7 @@ ...@@ -49,6 +50,7 @@
} }
}, },
"devDependencies": { "devDependencies": {
"@types/axios": "^0.14.0",
"@types/fs-extra": "^9.0.1", "@types/fs-extra": "^9.0.1",
"@types/node": "^14.0.5", "@types/node": "^14.0.5",
"@types/ws": "^7.2.4", "@types/ws": "^7.2.4",
......
...@@ -76,4 +76,16 @@ function resolveDirectory (dir: string, sharedFolder: string): ResolveDirectory ...@@ -76,4 +76,16 @@ function resolveDirectory (dir: string, sharedFolder: string): ResolveDirectory
return ret return ret
} }
export { absolutePath, relativePath, walkSync, resolveDirectory } /**
* returns the absolute path of the given @arg url
*
* @param {String} url - Remix-IDE URL instance
* @return {String} extracted domain name from url
*/
function getDomain(url: string) {
const domainMatch = url.match(/^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:\/\n?]+)/img)
return domainMatch ? domainMatch[0] : null
}
export { absolutePath, relativePath, walkSync, resolveDirectory, getDomain }
import * as WS from 'ws' import * as WS from 'ws'
import * as http from 'http' import * as http from 'http'
import { WebsocketOpt, SharedFolderClient } from '../types' import { WebsocketOpt, SharedFolderClient } from '../types'
import { getDomain } from './utils'
const { createClient } = require('@remixproject/plugin-ws') const { createClient } = require('@remixproject/plugin-ws')
...@@ -52,5 +53,24 @@ export default class WebSocket { ...@@ -52,5 +53,24 @@ export default class WebSocket {
} }
function originIsAllowed (origin: string, self: WebSocket): boolean { function originIsAllowed (origin: string, self: WebSocket): boolean {
return origin === self.opt.remixIdeUrl if (self.opt.remixIdeUrl) {
if (self.opt.remixIdeUrl.endsWith('/')) self.opt.remixIdeUrl = self.opt.remixIdeUrl.slice(0, -1)
return origin === self.opt.remixIdeUrl || origin === getDomain(self.opt.remixIdeUrl)
} else {
try {
const origins = require('../bin/origins.json')
const domain = getDomain(origin)
const { data } = origins
if (data.includes(origin) || data.includes(domain)) {
self.opt.remixIdeUrl = origin
console.log('\x1b[33m%s\x1b[0m', '[WARN] You may now only use IDE at ' + self.opt.remixIdeUrl + ' to connect to that instance')
return true
} else {
return false
}
} catch (e) {
return false
}
}
} }
...@@ -11,5 +11,7 @@ ...@@ -11,5 +11,7 @@
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
"emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
"skipLibCheck": true, "skipLibCheck": true,
} "resolveJsonModule": true
},
"include": ["bin", "src", "bin/origins.json"]
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment