@re-shell/cli
Version:
Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja
722 lines (721 loc) • 30.2 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.PluginSandbox = exports.PluginSecurityValidator = exports.SecurityLevel = void 0;
exports.createSecurityValidator = createSecurityValidator;
exports.createPluginSandbox = createPluginSandbox;
exports.getDefaultSecurityPolicy = getDefaultSecurityPolicy;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const crypto = __importStar(require("crypto"));
const events_1 = require("events");
// Security levels
var SecurityLevel;
(function (SecurityLevel) {
SecurityLevel["TRUSTED"] = "trusted";
SecurityLevel["VERIFIED"] = "verified";
SecurityLevel["SANDBOXED"] = "sandboxed";
SecurityLevel["RESTRICTED"] = "restricted";
SecurityLevel["BLOCKED"] = "blocked";
})(SecurityLevel || (exports.SecurityLevel = SecurityLevel = {}));
// Plugin security validator
class PluginSecurityValidator extends events_1.EventEmitter {
constructor(policy = {}) {
super();
this.trustedPublicKeys = new Set();
this.pluginReputations = new Map();
this.securityCache = new Map();
this.securityPolicy = {
allowNetworkAccess: false,
allowFileSystemAccess: true,
allowProcessExecution: false,
allowEnvironmentAccess: false,
allowWorkspaceAccess: true,
maxMemoryUsage: 512 * 1024 * 1024, // 512MB
maxExecutionTime: 30000, // 30 seconds
trustedSources: ['npm', 'builtin'],
blockedSources: [],
requiredSignatures: false,
...policy
};
}
// Perform comprehensive security scan
async scanPlugin(registration) {
const cacheKey = this.getCacheKey(registration);
// Check cache
if (this.securityCache.has(cacheKey)) {
const cached = this.securityCache.get(cacheKey);
this.emit('security-scan-cached', registration.manifest.name);
return cached;
}
this.emit('security-scan-started', registration.manifest.name);
const startTime = Date.now();
try {
const result = {
plugin: registration.manifest.name,
securityLevel: SecurityLevel.SANDBOXED,
violations: [],
permissions: registration.manifest.reshell?.permissions || [],
sandboxRequired: true,
approved: false,
warnings: []
};
// 1. Validate permissions
await this.validatePermissions(registration, result);
// 2. Scan for malicious code
await this.scanForMaliciousCode(registration, result);
// 3. Verify signatures
await this.verifySignature(registration, result);
// 4. Check reputation
await this.checkReputation(registration, result);
// 5. Analyze source trust
await this.analyzeSourceTrust(registration, result);
// 6. Determine security level
this.determineSecurityLevel(result);
// 7. Generate recommendations
this.generateSecurityRecommendations(result);
// Cache result
this.securityCache.set(cacheKey, result);
const duration = Date.now() - startTime;
this.emit('security-scan-completed', {
plugin: registration.manifest.name,
securityLevel: result.securityLevel,
violations: result.violations.length,
duration
});
return result;
}
catch (error) {
const duration = Date.now() - startTime;
this.emit('security-scan-failed', {
plugin: registration.manifest.name,
error,
duration
});
throw error;
}
}
// Validate plugin permissions
async validatePermissions(registration, result) {
const permissions = registration.manifest.reshell?.permissions || [];
for (const permission of permissions) {
const violation = this.checkPermissionViolation(permission);
if (violation) {
result.violations.push(violation);
}
}
// Check for excessive permissions
if (permissions.length > 10) {
result.violations.push({
type: 'permission',
severity: 'medium',
description: 'Plugin requests excessive permissions',
source: 'permission-validator',
recommendation: 'Review and reduce permission scope',
blocked: false
});
}
// Check for dangerous permission combinations
const hasFileSystem = permissions.some(p => p.type === 'filesystem' && p.access === 'full');
const hasNetwork = permissions.some(p => p.type === 'network');
const hasProcess = permissions.some(p => p.type === 'process');
if (hasFileSystem && hasNetwork && hasProcess) {
result.violations.push({
type: 'permission',
severity: 'high',
description: 'Plugin requests dangerous permission combination (filesystem + network + process)',
source: 'permission-validator',
recommendation: 'Consider sandboxing or restricting permissions',
blocked: true
});
}
}
// Check individual permission violation
checkPermissionViolation(permission) {
// Check against security policy
switch (permission.type) {
case 'network':
if (!this.securityPolicy.allowNetworkAccess) {
return {
type: 'permission',
severity: 'high',
description: `Network access not allowed: ${permission.description}`,
source: 'permission-policy',
recommendation: 'Remove network permission or enable network access',
blocked: true
};
}
break;
case 'process':
if (!this.securityPolicy.allowProcessExecution) {
return {
type: 'permission',
severity: 'high',
description: `Process execution not allowed: ${permission.description}`,
source: 'permission-policy',
recommendation: 'Remove process permission or enable process execution',
blocked: true
};
}
break;
case 'filesystem':
if (!this.securityPolicy.allowFileSystemAccess && permission.access !== 'read') {
return {
type: 'permission',
severity: 'medium',
description: `File system write access not allowed: ${permission.description}`,
source: 'permission-policy',
recommendation: 'Restrict to read-only access or enable filesystem writes',
blocked: false
};
}
break;
case 'environment':
if (!this.securityPolicy.allowEnvironmentAccess) {
return {
type: 'permission',
severity: 'medium',
description: `Environment access not allowed: ${permission.description}`,
source: 'permission-policy',
recommendation: 'Remove environment permission or enable environment access',
blocked: false
};
}
break;
}
return null;
}
// Scan for malicious code patterns
async scanForMaliciousCode(registration, result) {
try {
const mainFile = path.join(registration.pluginPath, registration.manifest.main);
if (!await fs.pathExists(mainFile)) {
result.violations.push({
type: 'malware',
severity: 'medium',
description: 'Main plugin file not found',
source: 'malware-scanner',
recommendation: 'Verify plugin integrity',
blocked: false
});
return;
}
const fileContent = await fs.readFile(mainFile, 'utf8');
// Check for suspicious patterns
const suspiciousPatterns = [
{ pattern: /eval\s*\(/g, severity: 'high', description: 'Uses eval() - potential code injection' },
{ pattern: /Function\s*\(/g, severity: 'medium', description: 'Uses Function constructor - potential code injection' },
{ pattern: /child_process|spawn|exec/g, severity: 'high', description: 'Executes system processes' },
{ pattern: /require\s*\(\s*['"`]fs['"`]\s*\)/g, severity: 'medium', description: 'Direct filesystem access' },
{ pattern: /require\s*\(\s*['"`]http['"`]\s*\)|require\s*\(\s*['"`]https['"`]\s*\)/g, severity: 'medium', description: 'Network access capabilities' },
{ pattern: /\.\.\/|\.\.\\|\.\.\//g, severity: 'medium', description: 'Path traversal attempt' },
{ pattern: /document\.cookie|localStorage|sessionStorage/g, severity: 'low', description: 'Browser storage access' },
{ pattern: /XMLHttpRequest|fetch\(/g, severity: 'low', description: 'HTTP requests' }
];
for (const { pattern, severity, description } of suspiciousPatterns) {
const matches = fileContent.match(pattern);
if (matches) {
result.violations.push({
type: 'malware',
severity,
description: `${description} (${matches.length} occurrences)`,
source: 'malware-scanner',
recommendation: severity === 'high' ? 'Block plugin execution' : 'Review code carefully',
blocked: severity === 'high'
});
}
}
// Check file size (very large files might be suspicious)
const stats = await fs.stat(mainFile);
if (stats.size > 10 * 1024 * 1024) { // 10MB
result.violations.push({
type: 'malware',
severity: 'medium',
description: `Unusually large plugin file (${Math.round(stats.size / 1024 / 1024)}MB)`,
source: 'malware-scanner',
recommendation: 'Verify plugin legitimacy',
blocked: false
});
}
// Check for minified/obfuscated code
const minifiedIndicators = [
fileContent.length > 1000 && fileContent.split('\n').length < 10, // Long lines
/[a-zA-Z0-9]{50,}/.test(fileContent), // Very long identifiers
fileContent.includes('\\x') && fileContent.includes('\\u') // Hex/unicode escapes
];
if (minifiedIndicators.some(Boolean)) {
result.violations.push({
type: 'malware',
severity: 'medium',
description: 'Plugin code appears to be minified or obfuscated',
source: 'malware-scanner',
recommendation: 'Review source code for transparency',
blocked: false
});
}
}
catch (error) {
result.warnings.push(`Could not scan plugin file: ${error instanceof Error ? error.message : String(error)}`);
}
}
// Verify plugin signature
async verifySignature(registration, result) {
// Look for signature file
const signatureFile = path.join(registration.pluginPath, 'SIGNATURE');
if (!await fs.pathExists(signatureFile)) {
if (this.securityPolicy.requiredSignatures) {
result.violations.push({
type: 'signature',
severity: 'high',
description: 'Plugin signature required but not found',
source: 'signature-validator',
recommendation: 'Sign the plugin or disable signature requirement',
blocked: true
});
}
return;
}
try {
const signatureData = await fs.readJSON(signatureFile);
result.signature = {
algorithm: signatureData.algorithm,
signature: signatureData.signature,
publicKey: signatureData.publicKey,
timestamp: signatureData.timestamp,
verified: false,
issuer: signatureData.issuer
};
// Verify signature (simplified - in real implementation would use proper crypto)
if (this.trustedPublicKeys.has(signatureData.publicKey)) {
result.signature.verified = true;
}
else {
result.violations.push({
type: 'signature',
severity: 'medium',
description: 'Plugin signature not from trusted source',
source: 'signature-validator',
recommendation: 'Verify signature issuer identity',
blocked: false
});
}
}
catch (error) {
result.violations.push({
type: 'signature',
severity: 'medium',
description: 'Invalid signature format',
source: 'signature-validator',
recommendation: 'Provide valid signature file',
blocked: false
});
}
}
// Check plugin reputation
async checkReputation(registration, result) {
const reputation = this.pluginReputations.get(registration.manifest.name);
if (reputation) {
result.reputation = reputation;
// Low reputation checks
if (reputation.rating < 2.0) {
result.violations.push({
type: 'permission',
severity: 'medium',
description: `Low community rating: ${reputation.rating}/5.0`,
source: 'reputation-checker',
recommendation: 'Consider alternative plugins',
blocked: false
});
}
if (reputation.downloads < 100) {
result.violations.push({
type: 'permission',
severity: 'low',
description: `Low download count: ${reputation.downloads}`,
source: 'reputation-checker',
recommendation: 'Exercise caution with new plugins',
blocked: false
});
}
// Check if recently updated
const daysSinceUpdate = (Date.now() - reputation.lastUpdated) / (1000 * 60 * 60 * 24);
if (daysSinceUpdate > 365) {
result.violations.push({
type: 'permission',
severity: 'low',
description: `Plugin not updated in ${Math.round(daysSinceUpdate)} days`,
source: 'reputation-checker',
recommendation: 'Verify plugin is still maintained',
blocked: false
});
}
}
else {
result.warnings.push('No reputation data available for plugin');
}
}
// Analyze source trust
async analyzeSourceTrust(registration, result) {
// Determine plugin source
const source = this.determinePluginSource(registration);
if (this.securityPolicy.blockedSources.includes(source)) {
result.violations.push({
type: 'permission',
severity: 'critical',
description: `Plugin from blocked source: ${source}`,
source: 'source-validator',
recommendation: 'Remove plugin or allow source',
blocked: true
});
}
if (!this.securityPolicy.trustedSources.includes(source)) {
result.violations.push({
type: 'permission',
severity: 'medium',
description: `Plugin from untrusted source: ${source}`,
source: 'source-validator',
recommendation: 'Verify source legitimacy',
blocked: false
});
}
}
// Determine plugin source type
determinePluginSource(registration) {
const pluginPath = registration.pluginPath;
if (pluginPath.includes('node_modules')) {
return 'npm';
}
else if (pluginPath.includes('.re-shell/plugins')) {
return 'local';
}
else if (pluginPath.includes('/plugins')) {
return 'builtin';
}
else {
return 'unknown';
}
}
// Determine overall security level
determineSecurityLevel(result) {
const criticalViolations = result.violations.filter(v => v.severity === 'critical');
const highViolations = result.violations.filter(v => v.severity === 'high');
const blockedViolations = result.violations.filter(v => v.blocked);
if (criticalViolations.length > 0 || blockedViolations.length > 0) {
result.securityLevel = SecurityLevel.BLOCKED;
result.approved = false;
}
else if (highViolations.length > 0) {
result.securityLevel = SecurityLevel.RESTRICTED;
result.approved = false;
result.sandboxRequired = true;
}
else if (result.violations.length > 0) {
result.securityLevel = SecurityLevel.SANDBOXED;
result.approved = true;
result.sandboxRequired = true;
}
else if (result.signature?.verified && result.reputation?.verified) {
result.securityLevel = SecurityLevel.TRUSTED;
result.approved = true;
result.sandboxRequired = false;
}
else {
result.securityLevel = SecurityLevel.VERIFIED;
result.approved = true;
result.sandboxRequired = false;
}
}
// Generate security recommendations
generateSecurityRecommendations(result) {
if (result.violations.length === 0) {
result.warnings.push('Plugin passed all security checks');
return;
}
const recommendations = new Set();
result.violations.forEach(violation => {
recommendations.add(violation.recommendation);
});
if (result.sandboxRequired) {
recommendations.add('Run plugin in sandboxed environment');
}
if (result.violations.some(v => v.severity === 'high' || v.severity === 'critical')) {
recommendations.add('Consider blocking plugin execution');
}
result.warnings.push(...Array.from(recommendations));
}
// Create sandbox configuration
createSandboxConfig(registration, securityResult) {
const baseConfig = {
isolateFileSystem: true,
isolateNetwork: true,
isolateProcesses: true,
memoryLimit: this.securityPolicy.maxMemoryUsage,
timeoutLimit: this.securityPolicy.maxExecutionTime,
allowedPaths: [
registration.pluginPath,
path.join(process.cwd(), '.re-shell', 'data', registration.manifest.name),
path.join(process.cwd(), '.re-shell', 'cache', registration.manifest.name)
],
blockedPaths: [
path.join(process.cwd(), '.re-shell', 'config.yaml'),
'/etc',
'/usr/bin',
'/System'
],
allowedNetworks: [],
blockedNetworks: ['127.0.0.1', 'localhost']
};
// Adjust based on permissions
const permissions = registration.manifest.reshell?.permissions || [];
permissions.forEach(permission => {
switch (permission.type) {
case 'filesystem':
if (permission.access === 'read') {
baseConfig.isolateFileSystem = false;
}
if (permission.resource) {
baseConfig.allowedPaths.push(permission.resource);
}
break;
case 'network':
baseConfig.isolateNetwork = false;
if (permission.resource) {
baseConfig.allowedNetworks.push(permission.resource);
}
break;
case 'process':
if (permission.access === 'read') {
baseConfig.isolateProcesses = false;
}
break;
}
});
// Adjust based on security level
switch (securityResult.securityLevel) {
case SecurityLevel.TRUSTED:
baseConfig.isolateFileSystem = false;
baseConfig.isolateNetwork = false;
break;
case SecurityLevel.VERIFIED:
baseConfig.isolateNetwork = false;
break;
case SecurityLevel.RESTRICTED:
baseConfig.memoryLimit = Math.min(baseConfig.memoryLimit, 256 * 1024 * 1024); // 256MB
baseConfig.timeoutLimit = Math.min(baseConfig.timeoutLimit, 10000); // 10 seconds
break;
}
return baseConfig;
}
// Add trusted public key
addTrustedPublicKey(publicKey) {
this.trustedPublicKeys.add(publicKey);
this.clearCache();
}
// Update plugin reputation
updatePluginReputation(pluginName, reputation) {
this.pluginReputations.set(pluginName, reputation);
this.clearCache();
}
// Clear security cache
clearCache() {
this.securityCache.clear();
this.emit('cache-cleared');
}
// Get cache key
getCacheKey(registration) {
const contentHash = crypto
.createHash('sha256')
.update(JSON.stringify(registration.manifest))
.update(registration.pluginPath)
.digest('hex');
return `${registration.manifest.name}_${registration.manifest.version}_${contentHash}`;
}
// Get security statistics
getSecurityStats() {
const stats = {
totalScans: this.securityCache.size,
trustedKeys: this.trustedPublicKeys.size,
reputationData: this.pluginReputations.size,
securityLevels: {},
violationTypes: {}
};
for (const result of this.securityCache.values()) {
stats.securityLevels[result.securityLevel] =
(stats.securityLevels[result.securityLevel] || 0) + 1;
result.violations.forEach(violation => {
stats.violationTypes[violation.type] =
(stats.violationTypes[violation.type] || 0) + 1;
});
}
return stats;
}
}
exports.PluginSecurityValidator = PluginSecurityValidator;
// Plugin sandbox executor
class PluginSandbox extends events_1.EventEmitter {
constructor(config) {
super();
this.activeProcesses = new Map();
this.config = config;
}
// Execute plugin in sandbox
async executeInSandbox(pluginFunction, context, timeout) {
const executionTimeout = timeout || this.config.timeoutLimit;
const startTime = Date.now();
this.emit('sandbox-execution-started', {
timeout: executionTimeout,
memoryLimit: this.config.memoryLimit
});
try {
// Create isolated context
const sandboxedContext = this.createSandboxedContext(context);
// Execute with timeout
const result = await Promise.race([
Promise.resolve(pluginFunction(sandboxedContext)),
new Promise((_, reject) => setTimeout(() => reject(new Error('Plugin execution timeout')), executionTimeout))
]);
const duration = Date.now() - startTime;
this.emit('sandbox-execution-completed', {
duration,
memoryUsed: process.memoryUsage().heapUsed
});
return result;
}
catch (error) {
const duration = Date.now() - startTime;
this.emit('sandbox-execution-failed', {
duration,
error
});
throw error;
}
}
// Create sandboxed context
createSandboxedContext(originalContext) {
const sandboxedContext = { ...originalContext };
// Override dangerous functions
if (this.config.isolateFileSystem) {
sandboxedContext.fs = this.createSandboxedFS();
}
if (this.config.isolateNetwork) {
sandboxedContext.http = null;
sandboxedContext.https = null;
sandboxedContext.fetch = null;
}
if (this.config.isolateProcesses) {
sandboxedContext.child_process = null;
sandboxedContext.process = this.createSandboxedProcess();
}
return sandboxedContext;
}
// Create sandboxed filesystem interface
createSandboxedFS() {
const originalFS = require('fs-extra');
const sandboxedFS = { ...originalFS };
// Override write operations
const writeOperations = ['writeFile', 'writeFileSync', 'writeJSON', 'writeJSONSync', 'remove', 'removeSync'];
writeOperations.forEach(operation => {
sandboxedFS[operation] = (filePath, ...args) => {
if (!this.isPathAllowed(filePath)) {
throw new Error(`Filesystem access denied: ${filePath}`);
}
return originalFS[operation](filePath, ...args);
};
});
return sandboxedFS;
}
// Create sandboxed process interface
createSandboxedProcess() {
return {
env: {},
cwd: () => this.config.allowedPaths[0] || process.cwd(),
exit: () => { throw new Error('Process exit blocked in sandbox'); },
kill: () => { throw new Error('Process kill blocked in sandbox'); }
};
}
// Check if path is allowed
isPathAllowed(filePath) {
const normalizedPath = path.resolve(filePath);
// Check blocked paths
for (const blockedPath of this.config.blockedPaths) {
if (normalizedPath.startsWith(path.resolve(blockedPath))) {
return false;
}
}
// Check allowed paths
for (const allowedPath of this.config.allowedPaths) {
if (normalizedPath.startsWith(path.resolve(allowedPath))) {
return true;
}
}
return false;
}
// Monitor resource usage
monitorResourceUsage() {
const interval = setInterval(() => {
const memoryUsage = process.memoryUsage();
if (memoryUsage.heapUsed > this.config.memoryLimit) {
this.emit('memory-limit-exceeded', {
current: memoryUsage.heapUsed,
limit: this.config.memoryLimit
});
}
}, 1000);
// Clean up interval
setTimeout(() => clearInterval(interval), this.config.timeoutLimit);
}
}
exports.PluginSandbox = PluginSandbox;
// Utility functions
function createSecurityValidator(policy) {
return new PluginSecurityValidator(policy);
}
function createPluginSandbox(config) {
return new PluginSandbox(config);
}
function getDefaultSecurityPolicy() {
return {
allowNetworkAccess: false,
allowFileSystemAccess: true,
allowProcessExecution: false,
allowEnvironmentAccess: false,
allowWorkspaceAccess: true,
maxMemoryUsage: 512 * 1024 * 1024,
maxExecutionTime: 30000,
trustedSources: ['npm', 'builtin'],
blockedSources: [],
requiredSignatures: false
};
}