butlerx
5 years ago
53 changed files with 1117 additions and 4539 deletions
@ -1,5 +0,0 @@ |
|||
{ |
|||
"presets": ["@babel/preset-typescript", ["@babel/env"]], |
|||
"compact": true, |
|||
"plugins": ["lodash", "@babel/plugin-proposal-class-properties"] |
|||
} |
@ -1,45 +0,0 @@ |
|||
module.exports = { |
|||
parser: '@typescript-eslint/parser', |
|||
plugins: ['@typescript-eslint', 'prettier'], |
|||
env: { |
|||
es6: true, |
|||
node: true, |
|||
browser: true, |
|||
}, |
|||
root: true, |
|||
extends: [ |
|||
'airbnb-base', |
|||
'plugin:@typescript-eslint/recommended', |
|||
'prettier', |
|||
'prettier/@typescript-eslint', |
|||
], |
|||
rules: { |
|||
'linebreak-style': ['error', 'unix'], |
|||
'arrow-parens': ['error', 'as-needed'], |
|||
'no-param-reassign': ['error', { props: false }], |
|||
'func-style': ['error', 'declaration', { allowArrowFunctions: true }], |
|||
'no-use-before-define': ['error', { functions: false }], |
|||
'@typescript-eslint/no-use-before-define': ['error', { functions: false }], |
|||
'import/prefer-default-export': 'off', |
|||
'lines-between-class-members': [ |
|||
'error', |
|||
'always', |
|||
{ exceptAfterSingleLine: true }, |
|||
], |
|||
'import/extensions': [ |
|||
'error', |
|||
'always', |
|||
{ |
|||
js: 'ignorePackages', |
|||
ts: 'never', |
|||
}, |
|||
], |
|||
}, |
|||
settings: { |
|||
'import/resolver': { |
|||
node: { |
|||
extensions: ['.ts', '.js'], |
|||
}, |
|||
}, |
|||
}, |
|||
}; |
@ -0,0 +1,68 @@ |
|||
{ |
|||
"parser": "@typescript-eslint/parser", |
|||
"plugins": ["@typescript-eslint", "prettier"], |
|||
"env": { |
|||
"es6": true, |
|||
"node": true, |
|||
"browser": true |
|||
}, |
|||
"root": true, |
|||
"extends": [ |
|||
"airbnb-base", |
|||
"plugin:@typescript-eslint/recommended", |
|||
"prettier", |
|||
"prettier/@typescript-eslint" |
|||
], |
|||
"rules": { |
|||
"linebreak-style": ["error", "unix"], |
|||
"arrow-parens": ["error", "as-needed"], |
|||
"no-param-reassign": [ |
|||
"error", |
|||
{ |
|||
"props": false |
|||
} |
|||
], |
|||
"func-style": [ |
|||
"error", |
|||
"declaration", |
|||
{ |
|||
"allowArrowFunctions": true |
|||
} |
|||
], |
|||
"no-use-before-define": [ |
|||
"error", |
|||
{ |
|||
"functions": false |
|||
} |
|||
], |
|||
"@typescript-eslint/no-use-before-define": [ |
|||
"error", |
|||
{ |
|||
"functions": false |
|||
} |
|||
], |
|||
"import/prefer-default-export": "off", |
|||
"lines-between-class-members": [ |
|||
"error", |
|||
"always", |
|||
{ |
|||
"exceptAfterSingleLine": true |
|||
} |
|||
], |
|||
"import/extensions": [ |
|||
"error", |
|||
"always", |
|||
{ |
|||
"js": "ignorePackages", |
|||
"ts": "never" |
|||
} |
|||
] |
|||
}, |
|||
"settings": { |
|||
"import/resolver": { |
|||
"node": { |
|||
"extensions": [".ts", ".js"] |
|||
} |
|||
} |
|||
} |
|||
} |
@ -1,19 +1,115 @@ |
|||
lib-cov |
|||
*.seed |
|||
./lib |
|||
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/node |
|||
# Edit at https://www.toptal.com/developers/gitignore?templates=node |
|||
|
|||
### Node ### |
|||
# Logs |
|||
logs |
|||
*.log |
|||
*.csv |
|||
*.dat |
|||
*.out |
|||
*.pid |
|||
*.gz |
|||
npm-debug.log* |
|||
yarn-debug.log* |
|||
yarn-error.log* |
|||
lerna-debug.log* |
|||
|
|||
# Diagnostic reports (https://nodejs.org/api/report.html) |
|||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json |
|||
|
|||
tmp |
|||
# Runtime data |
|||
pids |
|||
logs |
|||
results |
|||
*.pid |
|||
*.seed |
|||
*.pid.lock |
|||
|
|||
# Directory for instrumented libs generated by jscoverage/JSCover |
|||
lib-cov |
|||
|
|||
# Coverage directory used by tools like istanbul |
|||
coverage |
|||
*.lcov |
|||
|
|||
# nyc test coverage |
|||
.nyc_output |
|||
|
|||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) |
|||
.grunt |
|||
|
|||
# Bower dependency directory (https://bower.io/) |
|||
bower_components |
|||
|
|||
# node-waf configuration |
|||
.lock-wscript |
|||
|
|||
# Compiled binary addons (https://nodejs.org/api/addons.html) |
|||
build/Release |
|||
|
|||
# Dependency directories |
|||
node_modules/ |
|||
jspm_packages/ |
|||
|
|||
npm-debug.log |
|||
node_modules |
|||
.esm-cache |
|||
# TypeScript v1 declaration files |
|||
typings/ |
|||
|
|||
# TypeScript cache |
|||
*.tsbuildinfo |
|||
|
|||
# Optional npm cache directory |
|||
.npm |
|||
|
|||
# Optional eslint cache |
|||
.eslintcache |
|||
|
|||
# Microbundle cache |
|||
.rpt2_cache/ |
|||
.rts2_cache_cjs/ |
|||
.rts2_cache_es/ |
|||
.rts2_cache_umd/ |
|||
|
|||
# Optional REPL history |
|||
.node_repl_history |
|||
|
|||
# Output of 'npm pack' |
|||
*.tgz |
|||
|
|||
# Yarn Integrity file |
|||
.yarn-integrity |
|||
|
|||
# dotenv environment variables file |
|||
.env |
|||
.env.test |
|||
|
|||
# parcel-bundler cache (https://parceljs.org/) |
|||
.cache |
|||
|
|||
# Next.js build output |
|||
.next |
|||
|
|||
# Nuxt.js build / generate output |
|||
.nuxt |
|||
dist |
|||
.idea |
|||
|
|||
# Gatsby files |
|||
.cache/ |
|||
# Comment in the public line in if your project uses Gatsby and not Next.js |
|||
# https://nextjs.org/blog/next-9-1#public-directory-support |
|||
# public |
|||
|
|||
# vuepress build output |
|||
.vuepress/dist |
|||
|
|||
# Serverless directories |
|||
.serverless/ |
|||
|
|||
# FuseBox cache |
|||
.fusebox/ |
|||
|
|||
# DynamoDB Local files |
|||
.dynamodb/ |
|||
|
|||
# TernJS port file |
|||
.tern-port |
|||
|
|||
# Stores VSCode versions used for testing VSCode extensions |
|||
.vscode-test |
|||
|
|||
# End of https://www.toptal.com/developers/gitignore/api/node |
|||
|
@ -1,13 +0,0 @@ |
|||
module.exports = { |
|||
singleQuote: true, |
|||
trailingComma: 'es5', |
|||
proseWrap: 'always', |
|||
overrides: [ |
|||
{ |
|||
files: ['*.js', '*.ts'], |
|||
options: { |
|||
printWidth: 80, |
|||
}, |
|||
}, |
|||
], |
|||
}; |
@ -0,0 +1,13 @@ |
|||
{ |
|||
"singleQuote": true, |
|||
"trailingComma": "es5", |
|||
"proseWrap": "always", |
|||
"overrides": [ |
|||
{ |
|||
"files": ["*.js", "*.ts"], |
|||
"options": { |
|||
"printWidth": 80 |
|||
} |
|||
} |
|||
] |
|||
} |
@ -1,124 +0,0 @@ |
|||
#! /usr/bin/env node
|
|||
/* eslint-disable @typescript-eslint/no-var-requires, import/no-unresolved */ |
|||
|
|||
const yargs = require('yargs'); |
|||
const wetty = require('./dist').default; |
|||
|
|||
module.exports = wetty.wetty; |
|||
|
|||
/** |
|||
* Check if being run by cli or require |
|||
*/ |
|||
if (require.main === module) { |
|||
wetty.init( |
|||
yargs |
|||
.options({ |
|||
sslkey: { |
|||
demand: false, |
|||
type: 'string', |
|||
description: 'path to SSL key', |
|||
}, |
|||
sslcert: { |
|||
demand: false, |
|||
type: 'string', |
|||
description: 'path to SSL certificate', |
|||
}, |
|||
sshhost: { |
|||
demand: false, |
|||
description: 'ssh server host', |
|||
type: 'string', |
|||
default: process.env.SSHHOST || 'localhost', |
|||
}, |
|||
sshport: { |
|||
demand: false, |
|||
description: 'ssh server port', |
|||
type: 'number', |
|||
default: parseInt(process.env.SSHPORT, 10) || 22, |
|||
}, |
|||
sshuser: { |
|||
demand: false, |
|||
description: 'ssh user', |
|||
type: 'string', |
|||
default: process.env.SSHUSER || '', |
|||
}, |
|||
title: { |
|||
demand: false, |
|||
description: 'window title', |
|||
type: 'string', |
|||
default: process.env.TITLE || 'WeTTy - The Web Terminal Emulator', |
|||
}, |
|||
sshauth: { |
|||
demand: false, |
|||
description: |
|||
'defaults to "password", you can use "publickey,password" instead', |
|||
type: 'string', |
|||
default: process.env.SSHAUTH || 'password', |
|||
}, |
|||
sshpass: { |
|||
demand: false, |
|||
description: 'ssh password', |
|||
type: 'string', |
|||
default: process.env.SSHPASS || undefined, |
|||
}, |
|||
sshkey: { |
|||
demand: false, |
|||
description: |
|||
'path to an optional client private key (connection will be password-less and insecure!)', |
|||
type: 'string', |
|||
default: process.env.SSHKEY || undefined, |
|||
}, |
|||
forcessh: { |
|||
demand: false, |
|||
description: 'Connecting through ssh even if running as root', |
|||
type: 'boolean', |
|||
default: process.env.FORCESSH || false |
|||
}, |
|||
knownhosts: { |
|||
demand: false, |
|||
description: 'path to known hosts file', |
|||
type: 'string', |
|||
default: process.env.KNOWNHOSTS || '/dev/null', |
|||
}, |
|||
base: { |
|||
demand: false, |
|||
alias: 'b', |
|||
description: 'base path to wetty', |
|||
type: 'string', |
|||
default: process.env.BASE || '/wetty/', |
|||
}, |
|||
port: { |
|||
demand: false, |
|||
alias: 'p', |
|||
description: 'wetty listen port', |
|||
type: 'number', |
|||
default: parseInt(process.env.PORT, 10) || 3000, |
|||
}, |
|||
host: { |
|||
demand: false, |
|||
description: 'wetty listen host', |
|||
default: '0.0.0.0', |
|||
type: 'string', |
|||
}, |
|||
command: { |
|||
demand: false, |
|||
alias: 'c', |
|||
description: 'command to run in shell', |
|||
type: 'string', |
|||
default: process.env.COMMAND || 'login', |
|||
}, |
|||
bypasshelmet: { |
|||
demand: false, |
|||
description: 'disable helmet from placing security restrictions', |
|||
type: 'boolean', |
|||
default: false, |
|||
}, |
|||
help: { |
|||
demand: false, |
|||
alias: 'h', |
|||
type: 'boolean', |
|||
description: 'Print help message', |
|||
}, |
|||
}) |
|||
.boolean('allow_discovery').argv |
|||
); |
|||
} |
@ -0,0 +1,195 @@ |
|||
/* eslint-disable */ |
|||
|
|||
import { expect } from 'chai'; |
|||
import 'mocha'; |
|||
import * as sinon from 'sinon'; |
|||
|
|||
import { JSDOM } from 'jsdom'; |
|||
import { FileDownloader } from './download'; |
|||
|
|||
const { window } = new JSDOM(`...`); |
|||
|
|||
describe('FileDownloader', () => { |
|||
const FILE_BEGIN = 'BEGIN'; |
|||
const FILE_END = 'END'; |
|||
let fileDownloader: any; |
|||
|
|||
beforeEach(() => { |
|||
fileDownloader = new FileDownloader(() => {}, FILE_BEGIN, FILE_END); |
|||
}); |
|||
|
|||
afterEach(() => { |
|||
sinon.restore(); |
|||
}); |
|||
|
|||
it('should return data before file markers', () => { |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
expect( |
|||
fileDownloader.buffer(`DATA AT THE LEFT${FILE_BEGIN}FILE${FILE_END}`) |
|||
).to.equal('DATA AT THE LEFT'); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should return data after file markers', () => { |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
expect( |
|||
fileDownloader.buffer(`${FILE_BEGIN}FILE${FILE_END}DATA AT THE RIGHT`) |
|||
).to.equal('DATA AT THE RIGHT'); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should return data before and after file markers', () => { |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
expect( |
|||
fileDownloader.buffer( |
|||
`DATA AT THE LEFT${FILE_BEGIN}FILE${FILE_END}DATA AT THE RIGHT` |
|||
) |
|||
).to.equal('DATA AT THE LEFTDATA AT THE RIGHT'); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should return data before a beginning marker found', () => { |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
expect(fileDownloader.buffer(`DATA AT THE LEFT${FILE_BEGIN}FILE`)).to.equal( |
|||
'DATA AT THE LEFT' |
|||
); |
|||
}); |
|||
|
|||
it('should return data after an ending marker found', () => { |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
expect(fileDownloader.buffer(`${FILE_BEGIN}FI`)).to.equal(''); |
|||
expect(fileDownloader.buffer(`LE${FILE_END}DATA AT THE RIGHT`)).to.equal( |
|||
'DATA AT THE RIGHT' |
|||
); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should buffer across incomplete file begin marker sequence on two calls', () => { |
|||
fileDownloader = new FileDownloader(() => {}, 'BEGIN', 'END'); |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
|
|||
expect(fileDownloader.buffer('BEG')).to.equal(''); |
|||
expect(fileDownloader.buffer('INFILEEND')).to.equal(''); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should buffer across incomplete file begin marker sequence on n calls', () => { |
|||
fileDownloader = new FileDownloader(() => {}, 'BEGIN', 'END'); |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
|
|||
expect(fileDownloader.buffer('B')).to.equal(''); |
|||
expect(fileDownloader.buffer('E')).to.equal(''); |
|||
expect(fileDownloader.buffer('G')).to.equal(''); |
|||
expect(fileDownloader.buffer('I')).to.equal(''); |
|||
expect(fileDownloader.buffer('NFILE' + 'END')).to.equal(''); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should buffer across incomplete file begin marker sequence with data on the left and right on multiple calls', () => { |
|||
fileDownloader = new FileDownloader(() => {}, 'BEGIN', 'END'); |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
|
|||
expect(fileDownloader.buffer('DATA AT THE LEFT' + 'B')).to.equal( |
|||
'DATA AT THE LEFT' |
|||
); |
|||
expect(fileDownloader.buffer('E')).to.equal(''); |
|||
expect(fileDownloader.buffer('G')).to.equal(''); |
|||
expect(fileDownloader.buffer('I')).to.equal(''); |
|||
expect(fileDownloader.buffer('NFILE' + 'ENDDATA AT THE RIGHT')).to.equal( |
|||
'DATA AT THE RIGHT' |
|||
); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should buffer across incomplete file begin marker sequence then handle false positive', () => { |
|||
fileDownloader = new FileDownloader(() => {}, 'BEGIN', 'END'); |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
|
|||
expect(fileDownloader.buffer('DATA AT THE LEFT' + 'B')).to.equal( |
|||
'DATA AT THE LEFT' |
|||
); |
|||
expect(fileDownloader.buffer('E')).to.equal(''); |
|||
expect(fileDownloader.buffer('G')).to.equal(''); |
|||
// This isn't part of the file_begin marker and should trigger the partial
|
|||
// file begin marker to be returned with the normal data
|
|||
expect(fileDownloader.buffer('ZDATA AT THE RIGHT')).to.equal( |
|||
'BEGZDATA AT THE RIGHT' |
|||
); |
|||
expect(onCompleteFileStub.called).to.be.false; |
|||
}); |
|||
|
|||
it('should buffer across incomplete file end marker sequence on two calls', () => { |
|||
fileDownloader = new FileDownloader(() => {}, 'BEGIN', 'END'); |
|||
const mockFilePart1 = 'DATA AT THE LEFTBEGINFILEE'; |
|||
const mockFilePart2 = 'NDDATA AT THE RIGHT'; |
|||
|
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
expect(fileDownloader.buffer(mockFilePart1)).to.equal('DATA AT THE LEFT'); |
|||
expect(fileDownloader.buffer(mockFilePart2)).to.equal('DATA AT THE RIGHT'); |
|||
|
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should buffer across incomplete file end and file begin marker sequence with data on the left and right on multiple calls', () => { |
|||
fileDownloader = new FileDownloader(() => {}, 'BEGIN', 'END'); |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
|
|||
expect(fileDownloader.buffer('DATA AT THE LEFT' + 'BE')).to.equal( |
|||
'DATA AT THE LEFT' |
|||
); |
|||
expect(fileDownloader.buffer('G')).to.equal(''); |
|||
expect(fileDownloader.buffer('I')).to.equal(''); |
|||
expect(fileDownloader.buffer('NFILEE')).to.equal(''); |
|||
expect(fileDownloader.buffer('N')).to.equal(''); |
|||
expect(fileDownloader.buffer('DDATA AT THE RIGHT')).to.equal( |
|||
'DATA AT THE RIGHT' |
|||
); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should be able to handle multiple files', () => { |
|||
fileDownloader = new FileDownloader(() => {}, 'BEGIN', 'END'); |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
|
|||
expect( |
|||
fileDownloader.buffer( |
|||
'DATA AT THE LEFT' + 'BEGIN' + 'FILE1' + 'END' + 'SECOND DATA' + 'BEGIN' |
|||
) |
|||
).to.equal('DATA AT THE LEFT' + 'SECOND DATA'); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE1'); |
|||
|
|||
expect(fileDownloader.buffer('FILE2')).to.equal(''); |
|||
expect(fileDownloader.buffer('E')).to.equal(''); |
|||
expect(fileDownloader.buffer('NDRIGHT')).to.equal('RIGHT'); |
|||
expect(onCompleteFileStub.calledTwice).to.be.true; |
|||
expect(onCompleteFileStub.getCall(1).args[0]).to.equal('FILE2'); |
|||
}); |
|||
|
|||
it('should be able to handle multiple files with an ending marker', () => { |
|||
fileDownloader = new FileDownloader(() => {}, 'BEGIN', 'END'); |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
|
|||
expect( |
|||
fileDownloader.buffer('DATA AT THE LEFT' + 'BEGIN' + 'FILE1' + 'EN') |
|||
).to.equal('DATA AT THE LEFT'); |
|||
expect(onCompleteFileStub.calledOnce).to.be.false; |
|||
expect( |
|||
fileDownloader.buffer('D' + 'SECOND DATA' + 'BEGIN' + 'FILE2' + 'EN') |
|||
).to.equal('SECOND DATA'); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE1'); |
|||
expect(fileDownloader.buffer('D')).to.equal(''); |
|||
expect(onCompleteFileStub.calledTwice).to.be.true; |
|||
expect(onCompleteFileStub.getCall(1).args[0]).to.equal('FILE2'); |
|||
}); |
|||
}); |
@ -1,195 +0,0 @@ |
|||
/* eslint-disable */ |
|||
|
|||
import { expect } from 'chai'; |
|||
import 'mocha'; |
|||
import * as sinon from 'sinon'; |
|||
|
|||
import { JSDOM } from 'jsdom'; |
|||
import { FileDownloader } from '../download'; |
|||
|
|||
const { window } = new JSDOM(`...`); |
|||
|
|||
describe('FileDownloader', () => { |
|||
const FILE_BEGIN = 'BEGIN'; |
|||
const FILE_END = 'END'; |
|||
let fileDownloader: any; |
|||
|
|||
beforeEach(() => { |
|||
fileDownloader = new FileDownloader(() => { }, FILE_BEGIN, FILE_END); |
|||
}); |
|||
|
|||
afterEach(() => { |
|||
sinon.restore(); |
|||
}); |
|||
|
|||
it('should return data before file markers', () => { |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
expect( |
|||
fileDownloader.buffer(`DATA AT THE LEFT${FILE_BEGIN}FILE${FILE_END}`) |
|||
).to.equal('DATA AT THE LEFT'); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should return data after file markers', () => { |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
expect( |
|||
fileDownloader.buffer(`${FILE_BEGIN}FILE${FILE_END}DATA AT THE RIGHT`) |
|||
).to.equal('DATA AT THE RIGHT'); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should return data before and after file markers', () => { |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
expect( |
|||
fileDownloader.buffer( |
|||
`DATA AT THE LEFT${FILE_BEGIN}FILE${FILE_END}DATA AT THE RIGHT` |
|||
) |
|||
).to.equal('DATA AT THE LEFTDATA AT THE RIGHT'); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should return data before a beginning marker found', () => { |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
expect(fileDownloader.buffer(`DATA AT THE LEFT${FILE_BEGIN}FILE`)).to.equal( |
|||
'DATA AT THE LEFT' |
|||
); |
|||
}); |
|||
|
|||
it('should return data after an ending marker found', () => { |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
expect(fileDownloader.buffer(`${FILE_BEGIN}FI`)).to.equal(''); |
|||
expect(fileDownloader.buffer(`LE${FILE_END}DATA AT THE RIGHT`)).to.equal( |
|||
'DATA AT THE RIGHT' |
|||
); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should buffer across incomplete file begin marker sequence on two calls', () => { |
|||
fileDownloader = new FileDownloader(() => { }, 'BEGIN', 'END'); |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
|
|||
expect(fileDownloader.buffer('BEG')).to.equal(''); |
|||
expect(fileDownloader.buffer('INFILEEND')).to.equal(''); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should buffer across incomplete file begin marker sequence on n calls', () => { |
|||
fileDownloader = new FileDownloader(() => { }, 'BEGIN', 'END'); |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
|
|||
expect(fileDownloader.buffer('B')).to.equal(''); |
|||
expect(fileDownloader.buffer('E')).to.equal(''); |
|||
expect(fileDownloader.buffer('G')).to.equal(''); |
|||
expect(fileDownloader.buffer('I')).to.equal(''); |
|||
expect(fileDownloader.buffer('NFILE' + 'END')).to.equal(''); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should buffer across incomplete file begin marker sequence with data on the left and right on multiple calls', () => { |
|||
fileDownloader = new FileDownloader(() => { }, 'BEGIN', 'END'); |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
|
|||
expect(fileDownloader.buffer('DATA AT THE LEFT' + 'B')).to.equal( |
|||
'DATA AT THE LEFT' |
|||
); |
|||
expect(fileDownloader.buffer('E')).to.equal(''); |
|||
expect(fileDownloader.buffer('G')).to.equal(''); |
|||
expect(fileDownloader.buffer('I')).to.equal(''); |
|||
expect(fileDownloader.buffer('NFILE' + 'ENDDATA AT THE RIGHT')).to.equal( |
|||
'DATA AT THE RIGHT' |
|||
); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should buffer across incomplete file begin marker sequence then handle false positive', () => { |
|||
fileDownloader = new FileDownloader(() => { }, 'BEGIN', 'END'); |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
|
|||
expect(fileDownloader.buffer('DATA AT THE LEFT' + 'B')).to.equal( |
|||
'DATA AT THE LEFT' |
|||
); |
|||
expect(fileDownloader.buffer('E')).to.equal(''); |
|||
expect(fileDownloader.buffer('G')).to.equal(''); |
|||
// This isn't part of the file_begin marker and should trigger the partial
|
|||
// file begin marker to be returned with the normal data
|
|||
expect(fileDownloader.buffer('ZDATA AT THE RIGHT')).to.equal( |
|||
'BEGZDATA AT THE RIGHT' |
|||
); |
|||
expect(onCompleteFileStub.called).to.be.false; |
|||
}); |
|||
|
|||
it('should buffer across incomplete file end marker sequence on two calls', () => { |
|||
fileDownloader = new FileDownloader(() => { }, 'BEGIN', 'END'); |
|||
const mockFilePart1 = 'DATA AT THE LEFTBEGINFILEE'; |
|||
const mockFilePart2 = 'NDDATA AT THE RIGHT'; |
|||
|
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
expect(fileDownloader.buffer(mockFilePart1)).to.equal('DATA AT THE LEFT'); |
|||
expect(fileDownloader.buffer(mockFilePart2)).to.equal('DATA AT THE RIGHT'); |
|||
|
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should buffer across incomplete file end and file begin marker sequence with data on the left and right on multiple calls', () => { |
|||
fileDownloader = new FileDownloader(() => { }, 'BEGIN', 'END'); |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
|
|||
expect(fileDownloader.buffer('DATA AT THE LEFT' + 'BE')).to.equal( |
|||
'DATA AT THE LEFT' |
|||
); |
|||
expect(fileDownloader.buffer('G')).to.equal(''); |
|||
expect(fileDownloader.buffer('I')).to.equal(''); |
|||
expect(fileDownloader.buffer('NFILEE')).to.equal(''); |
|||
expect(fileDownloader.buffer('N')).to.equal(''); |
|||
expect(fileDownloader.buffer('DDATA AT THE RIGHT')).to.equal( |
|||
'DATA AT THE RIGHT' |
|||
); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE'); |
|||
}); |
|||
|
|||
it('should be able to handle multiple files', () => { |
|||
fileDownloader = new FileDownloader(() => { }, 'BEGIN', 'END'); |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
|
|||
expect( |
|||
fileDownloader.buffer( |
|||
'DATA AT THE LEFT' + 'BEGIN' + 'FILE1' + 'END' + 'SECOND DATA' + 'BEGIN' |
|||
) |
|||
).to.equal('DATA AT THE LEFT' + 'SECOND DATA'); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE1'); |
|||
|
|||
expect(fileDownloader.buffer('FILE2')).to.equal(''); |
|||
expect(fileDownloader.buffer('E')).to.equal(''); |
|||
expect(fileDownloader.buffer('NDRIGHT')).to.equal('RIGHT'); |
|||
expect(onCompleteFileStub.calledTwice).to.be.true; |
|||
expect(onCompleteFileStub.getCall(1).args[0]).to.equal('FILE2'); |
|||
}); |
|||
|
|||
it('should be able to handle multiple files with an ending marker', () => { |
|||
fileDownloader = new FileDownloader(() => { }, 'BEGIN', 'END'); |
|||
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile'); |
|||
|
|||
expect( |
|||
fileDownloader.buffer('DATA AT THE LEFT' + 'BEGIN' + 'FILE1' + 'EN') |
|||
).to.equal('DATA AT THE LEFT'); |
|||
expect(onCompleteFileStub.calledOnce).to.be.false; |
|||
expect( |
|||
fileDownloader.buffer('D' + 'SECOND DATA' + 'BEGIN' + 'FILE2' + 'EN') |
|||
).to.equal('SECOND DATA'); |
|||
expect(onCompleteFileStub.calledOnce).to.be.true; |
|||
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE1'); |
|||
expect(fileDownloader.buffer('D')).to.equal(''); |
|||
expect(onCompleteFileStub.calledTwice).to.be.true; |
|||
expect(onCompleteFileStub.getCall(1).args[0]).to.equal('FILE2'); |
|||
}); |
|||
}); |
@ -1,4 +0,0 @@ |
|||
export default function verifyPrompt(e: { returnValue: string }): string { |
|||
e.returnValue = 'Are you sure?'; |
|||
return e.returnValue; |
|||
} |
@ -0,0 +1,205 @@ |
|||
/** |
|||
* Create WeTTY server |
|||
* @module WeTTy |
|||
*/ |
|||
import * as yargs from 'yargs'; |
|||
import { isUndefined } from 'lodash'; |
|||
import { SSH, SSL, SSLBuffer, Server } from './shared/interfaces'; |
|||
import { getCommand } from './server/command'; |
|||
import { loadSSL } from './server/ssl'; |
|||
import { logger } from './shared/logger'; |
|||
import { login } from './server/login'; |
|||
import { server } from './server/socketServer'; |
|||
import { spawn } from './server/spawn'; |
|||
import { sshDefault, serverDefault, forceSSHDefault, defaultCommand} from './server/default'; |
|||
|
|||
|
|||
/** |
|||
* Check if being run by cli or require |
|||
*/ |
|||
if (require.main === module) { |
|||
const opts = yargs |
|||
.option('ssl-key', { |
|||
type: 'string', |
|||
description: 'path to SSL key', |
|||
}) |
|||
.option('ssl-cert', { |
|||
type: 'string', |
|||
description: 'path to SSL certificate', |
|||
}) |
|||
.option('ssh-host', { |
|||
description: 'ssh server host', |
|||
type: 'string', |
|||
default: sshDefault.host, |
|||
}) |
|||
.option('ssh-port', { |
|||
description: 'ssh server port', |
|||
type: 'number', |
|||
default: sshDefault.port, |
|||
}) |
|||
.option('ssh-user', { |
|||
description: 'ssh user', |
|||
type: 'string', |
|||
default: sshDefault.user, |
|||
}) |
|||
.option('title', { |
|||
description: 'window title', |
|||
type: 'string', |
|||
default: serverDefault.title, |
|||
}) |
|||
.option('ssh-auth', { |
|||
description: |
|||
'defaults to "password", you can use "publickey,password" instead', |
|||
type: 'string', |
|||
default: sshDefault.auth, |
|||
}) |
|||
.option('ssh-pass', { |
|||
description: 'ssh password', |
|||
type: 'string', |
|||
default: sshDefault.pass, |
|||
}) |
|||
.option('ssh-key', { |
|||
demand: false, |
|||
description: |
|||
'path to an optional client private key (connection will be password-less and insecure!)', |
|||
type: 'string', |
|||
default: sshDefault.key, |
|||
}) |
|||
.option('force-ssh', { |
|||
description: 'Connecting through ssh even if running as root', |
|||
type: 'boolean', |
|||
default: forceSSHDefault |
|||
}) |
|||
.option('known-hosts', { |
|||
description: 'path to known hosts file', |
|||
type: 'string', |
|||
default: sshDefault.knownHosts, |
|||
}) |
|||
.option('base', { |
|||
alias: 'b', |
|||
description: 'base path to wetty', |
|||
type: 'string', |
|||
default: serverDefault.base, |
|||
}) |
|||
.option('port', { |
|||
alias: 'p', |
|||
description: 'wetty listen port', |
|||
type: 'number', |
|||
default: serverDefault.port, |
|||
}) |
|||
.option('host', { |
|||
description: 'wetty listen host', |
|||
default: serverDefault.host, |
|||
type: 'string', |
|||
}) |
|||
.option('command', { |
|||
alias: 'c', |
|||
description: 'command to run in shell', |
|||
type: 'string', |
|||
default: defaultCommand, |
|||
}) |
|||
.option('bypass-helmet', { |
|||
description: 'disable helmet from placing security restrictions', |
|||
type: 'boolean', |
|||
default: serverDefault.bypassHelmet, |
|||
}) |
|||
.option('help', { |
|||
alias: 'h', |
|||
type: 'boolean', |
|||
description: 'Print help message', |
|||
}) |
|||
.boolean('allow_discovery').argv; |
|||
if (!opts.help) { |
|||
startServer( |
|||
{ |
|||
user: opts['ssh-user'], |
|||
host: opts['ssh-host'], |
|||
auth: opts['ssh-auth'], |
|||
port: opts['ssh-port'], |
|||
pass: opts['ssh-pass'], |
|||
key: opts['ssh-key'], |
|||
knownHosts: opts['known-hosts'], |
|||
}, |
|||
{ |
|||
base: opts.base, |
|||
host: opts.host, |
|||
port: opts.port, |
|||
title: opts.title, |
|||
bypassHelmet: opts['bypass-helmet'], |
|||
}, |
|||
opts.command, |
|||
opts['force-ssh'], |
|||
isUndefined(opts['ssl-key']) || isUndefined(opts['ssl-cert']) |
|||
? undefined |
|||
: { key: opts['ssl-key'], cert: opts['ssl-cert'] } |
|||
).catch(err => { |
|||
logger.error(err); |
|||
process.exitCode = 1; |
|||
}); |
|||
} else { |
|||
yargs.showHelp(); |
|||
process.exitCode = 0; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Starts WeTTy Server |
|||
* @name startServer |
|||
*/ |
|||
export async function startServer( |
|||
ssh: SSH = sshDefault, |
|||
serverConf: Server = serverDefault, |
|||
command:string = defaultCommand, |
|||
forcessh:boolean = forceSSHDefault, |
|||
ssl?: SSL |
|||
): Promise<SocketIO.Server> { |
|||
if (ssh.key) { |
|||
logger.warn(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|||
! Password-less auth enabled using private key from ${ssh.key}. |
|||
! This is dangerous, anything that reaches the wetty server |
|||
! will be able to run remote operations without authentication. |
|||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`);
|
|||
} |
|||
|
|||
const sslBuffer: SSLBuffer = await loadSSL(ssl); |
|||
const io = server(serverConf, sslBuffer); |
|||
/** |
|||
* Wetty server connected too |
|||
* @fires WeTTy#connnection |
|||
*/ |
|||
io.on('connection', async (socket: SocketIO.Socket) => { |
|||
/** |
|||
* @event wetty#connection |
|||
* @name connection |
|||
*/ |
|||
logger.info('Connection accepted.'); |
|||
const { args, user: sshUser } = getCommand( |
|||
socket, |
|||
ssh, |
|||
command, |
|||
forcessh |
|||
); |
|||
logger.debug('Command Generated', { |
|||
user: sshUser, |
|||
cmd: args.join(' '), |
|||
}); |
|||
|
|||
if (sshUser) { |
|||
spawn(socket, args); |
|||
} else { |
|||
try { |
|||
const username = await login(socket) |
|||
args[1] = `${username.trim()}@${args[1]}`; |
|||
logger.debug('Spawning term', { |
|||
username: username.trim(), |
|||
cmd: args.join(' ').trim(), |
|||
}); |
|||
spawn(socket, args); |
|||
}catch (error) { |
|||
logger.info('Disconnect signal sent'); |
|||
} |
|||
} |
|||
}); |
|||
return io |
|||
} |
|||
|
@ -1,18 +0,0 @@ |
|||
import * as yargs from 'yargs'; |
|||
import { logger } from '../utils'; |
|||
import WeTTy from '../wetty'; |
|||
import { CLI } from './options'; |
|||
import { unWrapArgs } from './parseArgs'; |
|||
|
|||
export default function init(opts: CLI): void { |
|||
if (!opts.help) { |
|||
const { ssh, server, command, forcessh, ssl } = unWrapArgs(opts); |
|||
WeTTy(ssh, server, command, forcessh, ssl).catch(err => { |
|||
logger.error(err); |
|||
process.exitCode = 1; |
|||
}); |
|||
} else { |
|||
yargs.showHelp(); |
|||
process.exitCode = 0; |
|||
} |
|||
} |
@ -1,22 +0,0 @@ |
|||
export interface Options { |
|||
sshhost: string; |
|||
sshport: number; |
|||
sshuser: string; |
|||
sshauth: string; |
|||
sshkey?: string; |
|||
sshpass?: string; |
|||
knownhosts: string; |
|||
sslkey?: string; |
|||
sslcert?: string; |
|||
base: string; |
|||
host: string; |
|||
port: number; |
|||
title: string; |
|||
command?: string; |
|||
forcessh?: boolean; |
|||
bypasshelmet?: boolean; |
|||
} |
|||
|
|||
export interface CLI extends Options { |
|||
help: boolean; |
|||
} |
@ -1,32 +0,0 @@ |
|||
import { isUndefined } from 'lodash'; |
|||
import { SSH, SSL, Server } from '../interfaces'; |
|||
import { Options } from './options'; |
|||
|
|||
export function unWrapArgs( |
|||
args: Options |
|||
): { ssh: SSH; server: Server; command?: string; forcessh?: boolean; ssl?: SSL } { |
|||
return { |
|||
ssh: { |
|||
user: args.sshuser, |
|||
host: args.sshhost, |
|||
auth: args.sshauth, |
|||
port: args.sshport, |
|||
pass: args.sshpass, |
|||
key: args.sshkey, |
|||
knownhosts: args.knownhosts, |
|||
}, |
|||
server: { |
|||
base: args.base, |
|||
host: args.host, |
|||
port: args.port, |
|||
title: args.title, |
|||
bypasshelmet: args.bypasshelmet || false, |
|||
}, |
|||
command: args.command, |
|||
forcessh: args.forcessh, |
|||
ssl: |
|||
isUndefined(args.sslkey) || isUndefined(args.sslcert) |
|||
? undefined |
|||
: { key: args.sslkey, cert: args.sslcert }, |
|||
}; |
|||
} |
@ -1,6 +1,6 @@ |
|||
import { isUndefined } from 'lodash'; |
|||
|
|||
export default function parseCommand(command: string, path?: string): string { |
|||
export function parseCommand(command: string, path?: string): string { |
|||
if (command === 'login' && isUndefined(path)) return ''; |
|||
return !isUndefined(path) |
|||
? `$SHELL -c "cd ${path};${command === 'login' ? '$SHELL' : command}"` |
@ -0,0 +1,22 @@ |
|||
import { SSH, Server } from '../shared/interfaces'; |
|||
|
|||
export const sshDefault:SSH = { |
|||
user: process.env.SSHUSER || '', |
|||
host: process.env.SSHHOST || 'localhost', |
|||
auth: process.env.SSHAUTH || 'password', |
|||
pass: process.env.SSHPASS || undefined, |
|||
key: process.env.SSHKEY || undefined, |
|||
port: parseInt(process.env.SSHPORT || '22', 10), |
|||
knownHosts: process.env.KNOWNHOSTS || '/dev/null', |
|||
} |
|||
|
|||
export const serverDefault: Server = { |
|||
base: process.env.BASE || '/wetty/', |
|||
port: parseInt(process.env.PORT || '3000', 10), |
|||
host: '0.0.0.0', |
|||
title: process.env.TITLE || 'WeTTy - The Web Terminal Emulator', |
|||
bypassHelmet: false, |
|||
}; |
|||
|
|||
export const forceSSHDefault = process.env.FORCESSH === 'true' || false; |
|||
export const defaultCommand = process.env.COMMAND || 'login' |
@ -1,4 +0,0 @@ |
|||
import WeTTy from './wetty'; |
|||
import init from './cli'; |
|||
|
|||
export default { start: WeTTy, init }; |
@ -1,5 +1,5 @@ |
|||
import { spawn } from 'node-pty'; |
|||
import { xterm } from './xterm'; |
|||
import { xterm } from './shared/xterm'; |
|||
|
|||
export function login(socket: SocketIO.Socket): Promise<string> { |
|||
// Check request-header for username
|
@ -1,13 +1,10 @@ |
|||
import { isUndefined } from 'lodash'; |
|||
import { spawn } from 'node-pty'; |
|||
import { logger } from '../../utils'; |
|||
import { xterm } from './xterm'; |
|||
import { spawn as spawnTerm } from 'node-pty'; |
|||
import { logger } from '../shared/logger'; |
|||
import { xterm } from './shared/xterm'; |
|||
|
|||
export default function spawnTerm( |
|||
socket: SocketIO.Socket, |
|||
args: string[] |
|||
): void { |
|||
const term = spawn('/usr/bin/env', args, xterm); |
|||
export function spawn(socket: SocketIO.Socket, args: string[]): void { |
|||
const term = spawnTerm('/usr/bin/env', args, xterm); |
|||
const { pid } = term; |
|||
const address = args[0] === 'ssh' ? args[1] : 'localhost'; |
|||
logger.info('Process Started on behalf of user', { |
@ -1,7 +1,7 @@ |
|||
import { readFile } from 'fs-extra'; |
|||
import { resolve } from 'path'; |
|||
import { isUndefined } from 'lodash'; |
|||
import { SSL, SSLBuffer } from '../interfaces'; |
|||
import { SSL, SSLBuffer } from '../shared/interfaces'; |
|||
|
|||
export async function loadSSL(ssl?: SSL): Promise<SSLBuffer> { |
|||
if (isUndefined(ssl) || isUndefined(ssl.key) || isUndefined(ssl.cert)) |
@ -1,5 +0,0 @@ |
|||
import logger from './logger'; |
|||
|
|||
export { logger }; |
|||
|
|||
export * from './ssl'; |
@ -1,72 +0,0 @@ |
|||
/** |
|||
* Create WeTTY server |
|||
* @module WeTTy |
|||
*/ |
|||
import server from '../socketServer'; |
|||
import getCommand from '../command'; |
|||
import { login, spawn } from './term'; |
|||
import { loadSSL, logger } from '../utils'; |
|||
import { SSH, SSL, SSLBuffer, Server } from '../interfaces'; |
|||
|
|||
/** |
|||
* Starts WeTTy Server |
|||
* @name startWeTTy |
|||
*/ |
|||
export default function startWeTTy( |
|||
ssh: SSH = { user: '', host: 'localhost', auth: 'password', port: 22 }, |
|||
serverConf: Server = { |
|||
base: '/wetty/', |
|||
port: 3000, |
|||
host: '0.0.0.0', |
|||
title: 'WeTTy', |
|||
bypasshelmet: false, |
|||
}, |
|||
command = '', |
|||
forcessh = false, |
|||
ssl?: SSL |
|||
): Promise<void> { |
|||
return loadSSL(ssl).then((sslBuffer: SSLBuffer) => { |
|||
if (ssh.key) { |
|||
logger.warn(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|||
! Password-less auth enabled using private key from ${ssh.key}. |
|||
! This is dangerous, anything that reaches the wetty server |
|||
! will be able to run remote operations without authentication. |
|||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`);
|
|||
} |
|||
|
|||
const io = server(serverConf, sslBuffer); |
|||
/** |
|||
* Wetty server connected too |
|||
* @fires WeTTy#connnection |
|||
*/ |
|||
io.on('connection', (socket: SocketIO.Socket) => { |
|||
/** |
|||
* @event wetty#connection |
|||
* @name connection |
|||
*/ |
|||
logger.info('Connection accepted.'); |
|||
const { args, user: sshUser } = getCommand(socket, ssh, command, forcessh); |
|||
logger.debug('Command Generated', { |
|||
user: sshUser, |
|||
cmd: args.join(' '), |
|||
}); |
|||
|
|||
if (sshUser) { |
|||
spawn(socket, args); |
|||
} else { |
|||
login(socket) |
|||
.then((username: string) => { |
|||
args[1] = `${username.trim()}@${args[1]}`; |
|||
logger.debug('Spawning term', { |
|||
username: username.trim(), |
|||
cmd: args.join(' ').trim(), |
|||
}); |
|||
return spawn(socket, args); |
|||
}) |
|||
.catch(() => { |
|||
logger.info('Disconnect signal sent'); |
|||
}); |
|||
} |
|||
}); |
|||
}); |
|||
} |
@ -1,5 +0,0 @@ |
|||
import spawn from './spawn'; |
|||
|
|||
export { spawn }; |
|||
|
|||
export * from './login'; |
@ -0,0 +1,4 @@ |
|||
export function verifyPrompt(e: { returnValue: string }): string { |
|||
e.returnValue = 'Are you sure?'; |
|||
return e.returnValue; |
|||
} |
@ -1,13 +0,0 @@ |
|||
{ |
|||
"compilerOptions": { |
|||
"target": "es6", |
|||
"module": "commonjs", |
|||
"lib": ["es2015"], |
|||
"sourceMap": true, |
|||
"strict": true, |
|||
"moduleResolution": "node", |
|||
"typeRoots": ["node_module/@types"], |
|||
"outDir": "./dist" |
|||
}, |
|||
"include": ["./src/**/*.ts"] |
|||
} |
@ -0,0 +1,7 @@ |
|||
{ |
|||
"extends": "./esm.json", |
|||
"compilerOptions": { |
|||
"module": "CommonJS", |
|||
"outDir": "../lib/cjs" |
|||
} |
|||
} |
@ -0,0 +1,14 @@ |
|||
{ |
|||
"compilerOptions": { |
|||
"target": "ES2015", |
|||
"module": "ES2020", |
|||
"strict": true, |
|||
"sourceMap": true, |
|||
"esModuleInterop": true, |
|||
"declaration": true, |
|||
"forceConsistentCasingInFileNames": true, |
|||
"outDir": "../lib/esm", |
|||
"typeRoots": ["node_module/@types"] |
|||
}, |
|||
"include": ["../src/**/*.ts"] |
|||
} |
@ -1,137 +0,0 @@ |
|||
/* eslint-disable @typescript-eslint/explicit-function-return-type */ |
|||
|
|||
import MiniCssExtractPlugin from 'mini-css-extract-plugin'; |
|||
import nodeExternals from 'webpack-node-externals'; |
|||
import path from 'path'; |
|||
import webpack from 'webpack'; |
|||
|
|||
const template = override => ({ |
|||
mode: process.env.NODE_ENV || 'development', |
|||
resolve: { |
|||
modules: [path.resolve(__dirname, 'src'), 'node_modules'], |
|||
extensions: ['.ts', '.json', '.js', '.node'], |
|||
}, |
|||
|
|||
stats: { |
|||
colors: true, |
|||
}, |
|||
...override, |
|||
}); |
|||
|
|||
const entry = (folder, file) => |
|||
path.join(__dirname, 'src', folder, `${file}.ts`); |
|||
|
|||
const entries = (folder, files) => |
|||
Object.assign(...files.map(file => ({ [file]: entry(folder, file) }))); |
|||
|
|||
export default [ |
|||
template({ |
|||
entry: entries('server', ['index', 'buffer']), |
|||
target: 'node', |
|||
devtool: 'source-map', |
|||
output: { |
|||
path: path.resolve(__dirname, 'dist'), |
|||
libraryTarget: 'commonjs2', |
|||
filename: '[name].js', |
|||
}, |
|||
node: { |
|||
__filename: false, |
|||
__dirname: false, |
|||
}, |
|||
externals: [nodeExternals()], |
|||
module: { |
|||
rules: [ |
|||
{ |
|||
test: /\.ts$/, |
|||
use: { |
|||
loader: 'babel-loader', |
|||
options: { |
|||
presets: [ |
|||
'@babel/preset-typescript', |
|||
[ |
|||
'@babel/preset-env', |
|||
{ |
|||
targets: { |
|||
node: 'current', |
|||
}, |
|||
}, |
|||
], |
|||
], |
|||
plugins: ['lodash'], |
|||
}, |
|||
}, |
|||
}, |
|||
{ |
|||
test: /\.js$/, |
|||
use: ['source-map-loader'], |
|||
enforce: 'pre', |
|||
}, |
|||
], |
|||
}, |
|||
plugins: [new webpack.IgnorePlugin(/uws/)], |
|||
}), |
|||
template({ |
|||
entry: entries('client', ['index']), |
|||
output: { |
|||
path: path.resolve(__dirname, 'dist', 'client'), |
|||
filename: '[name].js', |
|||
}, |
|||
module: { |
|||
rules: [ |
|||
{ |
|||
test: /\.ts$/, |
|||
use: { |
|||
loader: 'babel-loader', |
|||
options: { |
|||
presets: [ |
|||
'@babel/preset-typescript', |
|||
[ |
|||
'@babel/preset-env', |
|||
{ |
|||
targets: { |
|||
browsers: ['last 2 versions', 'safari >= 7'], |
|||
}, |
|||
}, |
|||
], |
|||
], |
|||
plugins: ['lodash', '@babel/plugin-proposal-class-properties'], |
|||
}, |
|||
}, |
|||
}, |
|||
{ |
|||
test: /\.js$/, |
|||
use: ['source-map-loader'], |
|||
enforce: 'pre', |
|||
}, |
|||
{ |
|||
test: /\.scss$/, |
|||
use: [ |
|||
{ |
|||
loader: MiniCssExtractPlugin.loader, |
|||
}, |
|||
{ |
|||
loader: 'css-loader', |
|||
}, |
|||
{ |
|||
loader: 'sass-loader', |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
test: /\.(jpg|jpeg|png|gif|mp3|svg|ico)$/, |
|||
loader: 'file-loader', |
|||
options: { |
|||
name: '[name].[ext]', |
|||
}, |
|||
}, |
|||
], |
|||
}, |
|||
plugins: [ |
|||
new MiniCssExtractPlugin({ |
|||
filename: '[name].css', |
|||
chunkFilename: '[id].css', |
|||
}), |
|||
], |
|||
devtool: 'source-map', |
|||
}), |
|||
]; |
File diff suppressed because it is too large
Loading…
Reference in new issue