Skip to content

Commit

Permalink
fix: 🔒Fix an issue that users can browse outside the wd
Browse files Browse the repository at this point in the history
Resolve #16
  • Loading branch information
vivaxy committed Dec 28, 2017
1 parent 4f27246 commit d50c567
Show file tree
Hide file tree
Showing 12 changed files with 45 additions and 181 deletions.
21 changes: 6 additions & 15 deletions lib/application.js
Expand Up @@ -20,27 +20,23 @@ const EMPTY_STRING = '';
const USAGE_TRACKER_ID_KEY = 'usage-tracker-id';

const formatWatch = (value) => {
let result = value;
if (isNaN(Number(value))) {
result = 0;
return 0;
} else if (value < 0) {
result = 0;
return 0;
}
return result;
return value;
};

const formatDirectory = (value) => {
let absoluteWorkingDirectory = process.cwd();
const absoluteWorkingDirectory = process.cwd();
if (path.isAbsolute(value)) {
absoluteWorkingDirectory = value;
} else {
absoluteWorkingDirectory = path.join(absoluteWorkingDirectory, value);
return value;
}
return absoluteWorkingDirectory;
return path.join(absoluteWorkingDirectory, value);
};

const prepare = () => {

if (config.get(configKey.IS_DEBUG)) {
config.set(configKey.LOG_LEVEL, 0);
}
Expand Down Expand Up @@ -115,13 +111,10 @@ const prepare = () => {
};

module.exports = () => {

prepare();

if (!config.get(configKey.IS_DEBUG)) {

process.on('uncaughtException', (e) => {

new usageTracker.UsageTracker({
owner: config.get(configKey.USAGE_TRACKER_OWNER),
repo: config.get(configKey.USAGE_TRACKER_REPO),
Expand All @@ -140,13 +133,11 @@ module.exports = () => {
log.error(e.stack);
// still exit as uncaught exception
});

}

startServer((nativeServer) => {
if (config.get(configKey.WATCH) !== false) {
startWatcher(nativeServer);
}
});

};
78 changes: 12 additions & 66 deletions lib/build-file-list.js
Expand Up @@ -25,21 +25,12 @@ const HTML_EXTENSION = '.html';
* @returns {number} sort value
*/
const compare = (_a, _b) => {

let result = STILL;

if (_a < _b) {

result = AHEAD;

return AHEAD;
} else if (_a > _b) {

result = ABACK;

return ABACK;
}

return result;

return STILL;
};

/**
Expand All @@ -50,125 +41,80 @@ const compare = (_a, _b) => {
* @returns {String} html content
*/
module.exports = (files, pathname, absoluteWorkingDirectory) => {

const baseDir = join(absoluteWorkingDirectory, pathname);

let filesFiltered = files.filter((file) => {

const filesFiltered = files.filter((file) => {
if (file.indexOf('.') === 0) {

return false;

}
// #3 remove inaccessible files from file list
// win下access任何文件都会返回可访问
try {

if (isWindows) {

fs.statSync(join(baseDir, file));

} else {

fs.accessSync(join(baseDir, file), fs.R_OK);

}

} catch (e) {

return false;

}
return true;

});

filesFiltered.sort((a, b) => {

let _isDirectoryA = fs.lstatSync(join(baseDir, a)).isDirectory();
let _isDirectoryB = fs.lstatSync(join(baseDir, b)).isDirectory();

const _isDirectoryA = fs.lstatSync(join(baseDir, a)).isDirectory();
const _isDirectoryB = fs.lstatSync(join(baseDir, b)).isDirectory();
// order by priority
if (_isDirectoryA && _isDirectoryB) {

return compare(a, b);

}
if (_isDirectoryA && !_isDirectoryB) {

return AHEAD;

}
if (_isDirectoryB && !_isDirectoryA) {

return ABACK;

}

let _extensionA = extname(a);
let _extensionB = extname(b);
let _isHtmlA = _extensionA === HTML_EXTENSION;
let _isHtmlB = _extensionB === HTML_EXTENSION;

const _extensionA = extname(a);
const _extensionB = extname(b);
const _isHtmlA = _extensionA === HTML_EXTENSION;
const _isHtmlB = _extensionB === HTML_EXTENSION;
if (_isHtmlA && _isHtmlB) {

return compare(a, b);

}
if (_isHtmlA && !_isHtmlB) {

return AHEAD;

}
if (_isHtmlB && !_isHtmlA) {

return ABACK;

}

if (_extensionA === _extensionB) {

return compare(a, b);

}

return compare(a, b);

});

if (pathname !== '/') {

filesFiltered.unshift('..');

}

let list = filesFiltered.map((file) => {

let ext = extname(file);
const list = filesFiltered.map((file) => {
const ext = extname(file);
let _ext = 'other';

if (ext === HTML_EXTENSION) {

_ext = 'html';

}
if (fs.lstatSync(join(baseDir, file)).isDirectory()) {

_ext = 'dir';

}
if (file === '..') {

_ext = 'null';

}
return {
// remove http://ip:port to redirect to correct ip:port
href: join(pathname, file),
className: _ext,
fileName: file
};

});

return pug.compileFile(join(__dirname, '../res/list.pug'), {
Expand Down
2 changes: 1 addition & 1 deletion lib/config.js
Expand Up @@ -4,7 +4,7 @@
*/
'use strict';

let config = {
const config = {
isDebug: false,

port: 3000,
Expand Down
2 changes: 0 additions & 2 deletions lib/open-browser.js
Expand Up @@ -14,7 +14,6 @@ const logPrefix = require('../constant/log-prefix');
const configKey = require('../constant/config');

module.exports = () => {

// always use current ip address

const protocol = config.get(configKey.SSL) ? 'https:' : 'http:';
Expand All @@ -28,5 +27,4 @@ module.exports = () => {
// open browser succeeded
log.debug(logPrefix.BROWSER, execCommand, openUrl);
});

};
10 changes: 2 additions & 8 deletions lib/server.js
Expand Up @@ -18,9 +18,7 @@ const logPrefix = require('../constant/log-prefix');
const configKey = require('../constant/config');

module.exports = (callback) => {

let nativeServer;

const directory = config.get(configKey.DIRECTORY);
const watch = config.get(configKey.WATCH);

Expand Down Expand Up @@ -48,16 +46,14 @@ module.exports = (callback) => {
openBrowserFunction();
}
// hit `space` will open page in browser
let stdIn = process.stdin;
const stdIn = process.stdin;
stdIn.setEncoding('utf8');
stdIn.on('data', openBrowserFunction);
callback(nativeServer);
};

const listen = () => {

let port = config.get(configKey.PORT);

const port = config.get(configKey.PORT);
if (config.get(configKey.SSL)) {
pem.createCertificate({days: 1, selfSigned: true}, (err, keys) => {
if (err) {
Expand All @@ -72,7 +68,6 @@ module.exports = (callback) => {
nativeServer = server.listen(port, serverSuccessCallback)
.on('error', serverErrorCallback);
}

};

const middlewareList = [];
Expand All @@ -88,7 +83,6 @@ module.exports = (callback) => {

const routeFile = path.join(directory, 'here.js');
getFileStat(routeFile)((err, stat) => {

if (!err) {
if (stat.isFile()) {
middlewareList.push(require('../middleware/load-route')(server));
Expand Down
26 changes: 5 additions & 21 deletions lib/watcher.js
Expand Up @@ -20,55 +20,41 @@ const NOT_FOUNT_INDEX = -1;
const A_THOUSAND = 1000;

module.exports = (server) => {

const absoluteWorkingDirectory = config.get(configKey.DIRECTORY);
const interval = config.get(configKey.WATCH);

let socketList = [];
const socketList = [];

let io = socketServer(server);
const io = socketServer(server);

io.on('connection', (socket) => {

socketList.push(socket);
socket.on('disconnect', () => {

let index = socketList.indexOf(socket);

const index = socketList.indexOf(socket);
if (index > NOT_FOUNT_INDEX) {

socketList.splice(index, SPLICE_COUNT);

}

});

});

let reload = debounce(() => {

const reload = debounce(() => {
socketList.forEach((socket) => {

log.debug(logPrefix.WATCH, 'reload');
socket.emit('reload');

});
}, interval * A_THOUSAND);

let watcher = chokidar.watch(absoluteWorkingDirectory, {

const watcher = chokidar.watch(absoluteWorkingDirectory, {
ignored: [
'**/node_modules',
/[\/\\]\./,
'.git',
'.idea',
'.DS_Store'
]

});

watcher.on('all', (action, filePath) => {

log.debug(logPrefix.WATCH, action, path.relative(absoluteWorkingDirectory, filePath));
switch (action) {
case 'add':
Expand All @@ -78,11 +64,9 @@ module.exports = (server) => {
reload();
break;
}

});

watcher.on('ready', () => {
log.info(logPrefix.WATCH, 'ready,', 'reload in', interval, 'seconds');
});

};
6 changes: 0 additions & 6 deletions middleware/error.js
Expand Up @@ -7,17 +7,11 @@
const FALLBACK_CONTENT_TYPE = require('../lib/fallback-content-type');

module.exports = function* (next) {

try {

yield next;

} catch (e) {

this.status = 404;
this.body = e.stack;
this.type = FALLBACK_CONTENT_TYPE;

}

};

0 comments on commit d50c567

Please sign in to comment.