mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-21 14:06:00 +00:00
disallow shell scripting in web by default
This commit is contained in:
@@ -34,6 +34,7 @@ module.exports = {
|
|||||||
singleDatabase: connections.singleDatabase,
|
singleDatabase: connections.singleDatabase,
|
||||||
hideAppEditor: !!process.env.HIDE_APP_EDITOR,
|
hideAppEditor: !!process.env.HIDE_APP_EDITOR,
|
||||||
allowShellConnection: platformInfo.allowShellConnection,
|
allowShellConnection: platformInfo.allowShellConnection,
|
||||||
|
allowShellScripting: platformInfo.allowShellConnection,
|
||||||
permissions,
|
permissions,
|
||||||
...currentVersion,
|
...currentVersion,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ module.exports = {
|
|||||||
const res = [];
|
const res = [];
|
||||||
for (const packageName of _.union(files1, files2)) {
|
for (const packageName of _.union(files1, files2)) {
|
||||||
if (packageName == 'dist') continue;
|
if (packageName == 'dist') continue;
|
||||||
// if (!/^dbgate-plugin-.*$/.test(packageName)) continue;
|
if (!/^dbgate-plugin-.*$/.test(packageName)) continue;
|
||||||
try {
|
try {
|
||||||
if (packagedContent && packagedContent[packageName]) {
|
if (packagedContent && packagedContent[packageName]) {
|
||||||
const manifest = {
|
const manifest = {
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ const byline = require('byline');
|
|||||||
const socket = require('../utility/socket');
|
const socket = require('../utility/socket');
|
||||||
const { fork } = require('child_process');
|
const { fork } = require('child_process');
|
||||||
const { rundir, uploadsdir, pluginsdir, getPluginBackendPath, packagedPluginList } = require('../utility/directories');
|
const { rundir, uploadsdir, pluginsdir, getPluginBackendPath, packagedPluginList } = require('../utility/directories');
|
||||||
const { extractShellApiPlugins, extractShellApiFunctionName } = require('dbgate-tools');
|
const { extractShellApiPlugins, extractShellApiFunctionName, jsonScriptToJavascript } = require('dbgate-tools');
|
||||||
const { handleProcessCommunication } = require('../utility/processComm');
|
const { handleProcessCommunication } = require('../utility/processComm');
|
||||||
const processArgs = require('../utility/processArgs');
|
const processArgs = require('../utility/processArgs');
|
||||||
|
const platformInfo = require('../utility/platformInfo');
|
||||||
|
|
||||||
function extractPlugins(script) {
|
function extractPlugins(script) {
|
||||||
const requireRegex = /\s*\/\/\s*@require\s+([^\s]+)\s*\n/g;
|
const requireRegex = /\s*\/\/\s*@require\s+([^\s]+)\s*\n/g;
|
||||||
@@ -148,12 +149,18 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
start_meta: true,
|
start_meta: true,
|
||||||
async start({ script, isGeneratedScript }) {
|
async start({ script }) {
|
||||||
if (!isGeneratedScript && process.env.DISABLE_SHELL) {
|
const runid = uuidv1();
|
||||||
return { errorMessage: 'Shell is disabled' };
|
|
||||||
|
if (script.type == 'json') {
|
||||||
|
const js = jsonScriptToJavascript(script);
|
||||||
|
return this.startCore(runid, scriptTemplate(js, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!platformInfo.allowShellScripting) {
|
||||||
|
return { errorMessage: 'Shell scripting is not allowed' };
|
||||||
}
|
}
|
||||||
|
|
||||||
const runid = uuidv1();
|
|
||||||
return this.startCore(runid, scriptTemplate(script, false));
|
return this.startCore(runid, scriptTemplate(script, false));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ const platformInfo = {
|
|||||||
platform,
|
platform,
|
||||||
runningInWebpack: !!process.env.WEBPACK_DEV_SERVER_URL,
|
runningInWebpack: !!process.env.WEBPACK_DEV_SERVER_URL,
|
||||||
allowShellConnection: !!process.env.SHELL_CONNECTION || !!isElectron(),
|
allowShellConnection: !!process.env.SHELL_CONNECTION || !!isElectron(),
|
||||||
|
allowShellScripting: !!process.env.SHELL_SCRIPTING || !!isElectron(),
|
||||||
defaultKeyfile: path.join(os.homedir(), '.ssh/id_rsa'),
|
defaultKeyfile: path.join(os.homedir(), '.ssh/id_rsa'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
165
packages/tools/src/ScriptWriter.ts
Normal file
165
packages/tools/src/ScriptWriter.ts
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
import _uniq from 'lodash/uniq';
|
||||||
|
import { extractShellApiFunctionName, extractShellApiPlugins } from './packageTools';
|
||||||
|
|
||||||
|
export class ScriptWriter {
|
||||||
|
s = '';
|
||||||
|
packageNames: string[] = [];
|
||||||
|
varCount = 0;
|
||||||
|
|
||||||
|
constructor(varCount = '0') {
|
||||||
|
this.varCount = parseInt(varCount) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
allocVariable(prefix = 'var') {
|
||||||
|
this.varCount += 1;
|
||||||
|
return `${prefix}${this.varCount}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
_put(s = '') {
|
||||||
|
this.s += s;
|
||||||
|
this.s += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
endLine() {
|
||||||
|
this._put();
|
||||||
|
}
|
||||||
|
|
||||||
|
assignCore(variableName, functionName, props) {
|
||||||
|
this._put(`const ${variableName} = await ${functionName}(${JSON.stringify(props)});`);
|
||||||
|
}
|
||||||
|
|
||||||
|
assign(variableName, functionName, props) {
|
||||||
|
this.assignCore(variableName, extractShellApiFunctionName(functionName), props);
|
||||||
|
this.packageNames.push(...extractShellApiPlugins(functionName, props));
|
||||||
|
}
|
||||||
|
|
||||||
|
assignValue(variableName, jsonValue) {
|
||||||
|
this._put(`const ${variableName} = ${JSON.stringify(jsonValue)};`);
|
||||||
|
}
|
||||||
|
|
||||||
|
requirePackage(packageName) {
|
||||||
|
this.packageNames.push(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
copyStream(sourceVar, targetVar, colmapVar = null) {
|
||||||
|
if (colmapVar) {
|
||||||
|
this._put(`await dbgateApi.copyStream(${sourceVar}, ${targetVar}, {columns: ${colmapVar}});`);
|
||||||
|
} else {
|
||||||
|
this._put(`await dbgateApi.copyStream(${sourceVar}, ${targetVar});`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
comment(s) {
|
||||||
|
this._put(`// ${s}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getScript(schedule = null) {
|
||||||
|
const packageNames = this.packageNames;
|
||||||
|
let prefix = _uniq(packageNames)
|
||||||
|
.map(packageName => `// @require ${packageName}\n`)
|
||||||
|
.join('');
|
||||||
|
if (schedule) prefix += `// @schedule ${schedule}`;
|
||||||
|
if (prefix) prefix += '\n';
|
||||||
|
|
||||||
|
return prefix + this.s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ScriptWriterJson {
|
||||||
|
s = '';
|
||||||
|
packageNames: string[] = [];
|
||||||
|
varCount = 0;
|
||||||
|
commands = [];
|
||||||
|
|
||||||
|
constructor(varCount = '0') {
|
||||||
|
this.varCount = parseInt(varCount) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
allocVariable(prefix = 'var') {
|
||||||
|
this.varCount += 1;
|
||||||
|
return `${prefix}${this.varCount}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
endLine() {
|
||||||
|
this.commands.push({
|
||||||
|
type: 'endline',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
assign(variableName, functionName, props) {
|
||||||
|
this.commands.push({
|
||||||
|
type: 'assign',
|
||||||
|
variableName,
|
||||||
|
functionName: extractShellApiFunctionName(functionName),
|
||||||
|
props,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.packageNames.push(...extractShellApiPlugins(functionName, props));
|
||||||
|
}
|
||||||
|
|
||||||
|
assignValue(variableName, jsonValue) {
|
||||||
|
this.commands.push({
|
||||||
|
type: 'assignValue',
|
||||||
|
variableName,
|
||||||
|
jsonValue,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
copyStream(sourceVar, targetVar, colmapVar = null) {
|
||||||
|
this.commands.push({
|
||||||
|
type: 'copyStream',
|
||||||
|
sourceVar,
|
||||||
|
targetVar,
|
||||||
|
colmapVar,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
comment(text) {
|
||||||
|
this.commands.push({
|
||||||
|
type: 'comment',
|
||||||
|
text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getScript(schedule = null) {
|
||||||
|
return {
|
||||||
|
type: 'json',
|
||||||
|
schedule,
|
||||||
|
commands: this.commands,
|
||||||
|
packageNames: this.packageNames,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function jsonScriptToJavascript(json) {
|
||||||
|
const { schedule, commands, packageNames } = json;
|
||||||
|
const script = new ScriptWriter();
|
||||||
|
for (const packageName of packageNames) {
|
||||||
|
if (!/^dbgate-plugin-.*$/.test(packageName)) {
|
||||||
|
throw new Error('Unallowed package name:' + packageName);
|
||||||
|
}
|
||||||
|
script.packageNames.push(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const cmd of commands) {
|
||||||
|
switch (cmd.type) {
|
||||||
|
case 'assign':
|
||||||
|
script.assignCore(cmd.variableName, cmd.functionName, cmd.props);
|
||||||
|
break;
|
||||||
|
case 'assignValue':
|
||||||
|
script.assignValue(cmd.variableName, cmd.jsonValue);
|
||||||
|
break;
|
||||||
|
case 'copyStream':
|
||||||
|
script.copyStream(cmd.sourceVar, cmd.targetVar, cmd.colmapVar);
|
||||||
|
break;
|
||||||
|
case 'endLine':
|
||||||
|
script.endLine();
|
||||||
|
break;
|
||||||
|
case 'comment':
|
||||||
|
script.comment(cmd.text);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return script.getScript(schedule);
|
||||||
|
}
|
||||||
@@ -17,3 +17,4 @@ export * from './yamlModelConv';
|
|||||||
export * from './stringTools';
|
export * from './stringTools';
|
||||||
export * from './computeDiffRows';
|
export * from './computeDiffRows';
|
||||||
export * from './preloadedRowsTools';
|
export * from './preloadedRowsTools';
|
||||||
|
export * from './ScriptWriter';
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
import _ from 'lodash';
|
|
||||||
import { extractShellApiFunctionName, extractShellApiPlugins } from 'dbgate-tools';
|
|
||||||
|
|
||||||
export default class ScriptWriter {
|
|
||||||
s = '';
|
|
||||||
packageNames: string[] = [];
|
|
||||||
varCount = 0;
|
|
||||||
|
|
||||||
constructor(varCount = '0') {
|
|
||||||
this.varCount = parseInt(varCount) || 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
allocVariable(prefix = 'var') {
|
|
||||||
this.varCount += 1;
|
|
||||||
return `${prefix}${this.varCount}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
put(s = '') {
|
|
||||||
this.s += s;
|
|
||||||
this.s += '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
assign(variableName, functionName, props) {
|
|
||||||
this.put(`const ${variableName} = await ${extractShellApiFunctionName(functionName)}(${JSON.stringify(props)});`);
|
|
||||||
this.packageNames.push(...extractShellApiPlugins(functionName, props));
|
|
||||||
}
|
|
||||||
|
|
||||||
assignValue(variableName, jsonValue) {
|
|
||||||
this.put(`const ${variableName} = ${JSON.stringify(jsonValue)};`);
|
|
||||||
}
|
|
||||||
|
|
||||||
requirePackage(packageName) {
|
|
||||||
this.packageNames.push(packageName);
|
|
||||||
}
|
|
||||||
|
|
||||||
copyStream(sourceVar, targetVar, colmapVar = null) {
|
|
||||||
if (colmapVar) {
|
|
||||||
this.put(`await dbgateApi.copyStream(${sourceVar}, ${targetVar}, {columns: ${colmapVar}});`);
|
|
||||||
} else {
|
|
||||||
this.put(`await dbgateApi.copyStream(${sourceVar}, ${targetVar});`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
comment(s) {
|
|
||||||
this.put(`// ${s}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
getScript(schedule = null) {
|
|
||||||
const packageNames = this.packageNames;
|
|
||||||
let prefix = _.uniq(packageNames)
|
|
||||||
.map(packageName => `// @require ${packageName}\n`)
|
|
||||||
.join('');
|
|
||||||
if (schedule) prefix += `// @schedule ${schedule}`;
|
|
||||||
if (prefix) prefix += '\n';
|
|
||||||
|
|
||||||
return prefix + this.s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import ScriptWriter from './ScriptWriter';
|
import { ScriptWriter, ScriptWriterJson } from 'dbgate-tools';
|
||||||
import getAsArray from '../utility/getAsArray';
|
import getAsArray from '../utility/getAsArray';
|
||||||
import { getConnectionInfo } from '../utility/metadataLoaders';
|
import { getConnectionInfo } from '../utility/metadataLoaders';
|
||||||
import { findEngineDriver, findObjectLike } from 'dbgate-tools';
|
import { findEngineDriver, findObjectLike } from 'dbgate-tools';
|
||||||
@@ -187,8 +187,12 @@ export function normalizeExportColumnMap(colmap) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function createImpExpScript(extensions, values, addEditorInfo = true) {
|
export default async function createImpExpScript(extensions, values, addEditorInfo = true, forceScript = false) {
|
||||||
const script = new ScriptWriter(values.startVariableIndex || 0);
|
const config = getCurrentConfig();
|
||||||
|
const script =
|
||||||
|
config.allowShellScripting || forceScript
|
||||||
|
? new ScriptWriter(values.startVariableIndex || 0)
|
||||||
|
: new ScriptWriterJson(values.startVariableIndex || 0);
|
||||||
|
|
||||||
const [sourceConnection, sourceDriver] = await getConnection(
|
const [sourceConnection, sourceDriver] = await getConnection(
|
||||||
extensions,
|
extensions,
|
||||||
@@ -222,7 +226,7 @@ export default async function createImpExpScript(extensions, values, addEditorIn
|
|||||||
}
|
}
|
||||||
|
|
||||||
script.copyStream(sourceVar, targetVar, colmapVar);
|
script.copyStream(sourceVar, targetVar, colmapVar);
|
||||||
script.put();
|
script.endLine();
|
||||||
}
|
}
|
||||||
if (addEditorInfo) {
|
if (addEditorInfo) {
|
||||||
script.comment('@ImportExportConfigurator');
|
script.comment('@ImportExportConfigurator');
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
import { getDefaultFileFormat } from '../plugins/fileformats';
|
import { getDefaultFileFormat } from '../plugins/fileformats';
|
||||||
import RunnerOutputFiles from '../query/RunnerOutputFiles.svelte';
|
import RunnerOutputFiles from '../query/RunnerOutputFiles.svelte';
|
||||||
import SocketMessageView from '../query/SocketMessageView.svelte';
|
import SocketMessageView from '../query/SocketMessageView.svelte';
|
||||||
import { currentArchive, currentDatabase, extensions, selectedWidget } from '../stores';
|
import { currentArchive, currentDatabase, extensions, getCurrentConfig, selectedWidget } from '../stores';
|
||||||
import { apiCall, apiOff, apiOn } from '../utility/api';
|
import { apiCall, apiOff, apiOn } from '../utility/api';
|
||||||
import createRef from '../utility/createRef';
|
import createRef from '../utility/createRef';
|
||||||
import openNewTab from '../utility/openNewTab';
|
import openNewTab from '../utility/openNewTab';
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
|
|
||||||
const handleGenerateScript = async e => {
|
const handleGenerateScript = async e => {
|
||||||
closeCurrentModal();
|
closeCurrentModal();
|
||||||
const code = await createImpExpScript($extensions, e.detail);
|
const code = await createImpExpScript($extensions, e.detail, undefined, true);
|
||||||
openNewTab(
|
openNewTab(
|
||||||
{
|
{
|
||||||
title: 'Shell #',
|
title: 'Shell #',
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
const script = await createImpExpScript($extensions, values);
|
const script = await createImpExpScript($extensions, values);
|
||||||
executeNumber += 1;
|
executeNumber += 1;
|
||||||
let runid = runnerId;
|
let runid = runnerId;
|
||||||
const resp = await apiCall('runners/start', { script, isGeneratedScript: true });
|
const resp = await apiCall('runners/start', { script });
|
||||||
runid = resp.runid;
|
runid = resp.runid;
|
||||||
runnerId = runid;
|
runnerId = runid;
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
execute: true,
|
execute: true,
|
||||||
toggleComment: true,
|
toggleComment: true,
|
||||||
findReplace: true,
|
findReplace: true,
|
||||||
|
executeAdditionalCondition: () => getCurrentConfig().allowShellScripting,
|
||||||
});
|
});
|
||||||
|
|
||||||
registerCommand({
|
registerCommand({
|
||||||
@@ -51,6 +52,7 @@
|
|||||||
import AceEditor from '../query/AceEditor.svelte';
|
import AceEditor from '../query/AceEditor.svelte';
|
||||||
import RunnerOutputPane from '../query/RunnerOutputPane.svelte';
|
import RunnerOutputPane from '../query/RunnerOutputPane.svelte';
|
||||||
import useEditorData from '../query/useEditorData';
|
import useEditorData from '../query/useEditorData';
|
||||||
|
import { getCurrentConfig } from '../stores';
|
||||||
import { apiCall, apiOff, apiOn } from '../utility/api';
|
import { apiCall, apiOff, apiOn } from '../utility/api';
|
||||||
import { copyTextToClipboard } from '../utility/clipboard';
|
import { copyTextToClipboard } from '../utility/clipboard';
|
||||||
import { changeTab } from '../utility/common';
|
import { changeTab } from '../utility/common';
|
||||||
@@ -177,6 +179,11 @@
|
|||||||
const resp = await apiCall('runners/start', {
|
const resp = await apiCall('runners/start', {
|
||||||
script: getActiveScript(),
|
script: getActiveScript(),
|
||||||
});
|
});
|
||||||
|
if (resp.errorMessage) {
|
||||||
|
showSnackbarError(resp.errorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
runid = resp.runid;
|
runid = resp.runid;
|
||||||
runnerId = runid;
|
runnerId = runid;
|
||||||
busy = true;
|
busy = true;
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import ScriptWriter from '../impexp/ScriptWriter';
|
import { ScriptWriter, ScriptWriterJson } from 'dbgate-tools';
|
||||||
import getElectron from './getElectron';
|
import getElectron from './getElectron';
|
||||||
import { showSnackbar, showSnackbarInfo, showSnackbarError, closeSnackbar } from '../utility/snackbar';
|
import { showSnackbar, showSnackbarInfo, showSnackbarError, closeSnackbar } from '../utility/snackbar';
|
||||||
import resolveApi from './resolveApi';
|
import resolveApi from './resolveApi';
|
||||||
import { apiCall, apiOff, apiOn } from './api';
|
import { apiCall, apiOff, apiOn } from './api';
|
||||||
import { normalizeExportColumnMap } from '../impexp/createImpExpScript';
|
import { normalizeExportColumnMap } from '../impexp/createImpExpScript';
|
||||||
|
import { getCurrentConfig } from '../stores';
|
||||||
|
|
||||||
export async function exportQuickExportFile(dataName, reader, format, columnMap = null) {
|
export async function exportQuickExportFile(dataName, reader, format, columnMap = null) {
|
||||||
const electron = getElectron();
|
const electron = getElectron();
|
||||||
@@ -25,7 +26,7 @@ export async function exportQuickExportFile(dataName, reader, format, columnMap
|
|||||||
|
|
||||||
if (!filePath) return;
|
if (!filePath) return;
|
||||||
|
|
||||||
const script = new ScriptWriter();
|
const script = getCurrentConfig().allowShellScripting ? new ScriptWriter() : new ScriptWriterJson();
|
||||||
|
|
||||||
const sourceVar = script.allocVariable();
|
const sourceVar = script.allocVariable();
|
||||||
script.assign(sourceVar, reader.functionName, reader.props);
|
script.assign(sourceVar, reader.functionName, reader.props);
|
||||||
@@ -42,9 +43,9 @@ export async function exportQuickExportFile(dataName, reader, format, columnMap
|
|||||||
}
|
}
|
||||||
|
|
||||||
script.copyStream(sourceVar, targetVar, colmapVar);
|
script.copyStream(sourceVar, targetVar, colmapVar);
|
||||||
script.put();
|
script.endLine();
|
||||||
|
|
||||||
const resp = await apiCall('runners/start', { script: script.getScript(), isGeneratedScript: true });
|
const resp = await apiCall('runners/start', { script: script.getScript() });
|
||||||
const runid = resp.runid;
|
const runid = resp.runid;
|
||||||
let isCanceled = false;
|
let isCanceled = false;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user