Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

glm-coding

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

glm-coding - npm Package Compare versions

Comparing version
0.5.0
to
0.6.0
+17
dist/core/executor.d.ts
/**
* Code Executor Module
* Safely executes generated code with timeout and cleanup
*/
import type { ExecuteResult } from './types.js';
/**
* Infer language from file extension
*/
export declare function inferLanguageFromExtension(filename: string): string | null;
/**
* Execute code and return result
*/
export declare function executeCode(code: string, language: string, timeout?: number): Promise<ExecuteResult>;
/**
* Save blocked code to temp directory for inspection
*/
export declare function saveBlockedCode(code: string, language: string): Promise<string>;
/**
* Code Executor Module
* Safely executes generated code with timeout and cleanup
*/
import { execFile } from 'child_process';
import { writeFile, unlink, mkdir } from 'fs/promises';
import { tmpdir } from 'os';
import { join } from 'path';
import { promisify } from 'util';
const execFileAsync = promisify(execFile);
/**
* Default execution timeout in milliseconds (30 seconds)
*/
const DEFAULT_TIMEOUT = 30000;
/**
* Language to interpreter mapping
*/
const INTERPRETERS = {
python: ['python3', 'python'],
py: ['python3', 'python'],
node: ['node'],
javascript: ['node'],
js: ['node'],
bash: ['bash'],
sh: ['sh'],
};
/**
* File extensions for each language
*/
const EXTENSIONS = {
python: '.py',
py: '.py',
node: '.js',
javascript: '.js',
js: '.js',
bash: '.sh',
sh: '.sh',
};
/**
* Get GLM temp directory path
*/
function getGlmTempDir() {
return join(tmpdir(), 'glm-coding');
}
/**
* Generate timestamp string for filename
*/
function getTimestamp() {
const now = new Date();
const pad = (n) => n.toString().padStart(2, '0');
return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
}
/**
* Ensure GLM temp directory exists
*/
async function ensureTempDir() {
const dir = getGlmTempDir();
await mkdir(dir, { recursive: true });
return dir;
}
/**
* Find available interpreter for language
*/
async function findInterpreter(language) {
const interpreters = INTERPRETERS[language.toLowerCase()];
if (!interpreters) {
return null;
}
for (const interpreter of interpreters) {
try {
await execFileAsync('which', [interpreter]);
return interpreter;
}
catch {
// Interpreter not found, try next
}
}
return null;
}
/**
* Infer language from file extension
*/
export function inferLanguageFromExtension(filename) {
const ext = filename.split('.').pop()?.toLowerCase();
if (!ext)
return null;
const extToLang = {
py: 'python',
js: 'node',
sh: 'bash',
};
return extToLang[ext] || null;
}
/**
* Execute code and return result
*/
export async function executeCode(code, language, timeout = DEFAULT_TIMEOUT) {
const interpreter = await findInterpreter(language);
if (!interpreter) {
return {
exitCode: 1,
stdout: '',
stderr: `No interpreter found for language: ${language}`,
};
}
const ext = EXTENSIONS[language.toLowerCase()] || '.txt';
const tempDir = await ensureTempDir();
const tempFile = join(tempDir, `glm_temp_${getTimestamp()}${ext}`);
try {
// Write code to temp file
await writeFile(tempFile, code, 'utf-8');
// Execute with timeout
const { stdout, stderr } = await execFileAsync(interpreter, [tempFile], {
timeout,
maxBuffer: 10 * 1024 * 1024, // 10MB
});
return {
exitCode: 0,
stdout,
stderr,
};
}
catch (error) {
const err = error;
if (err.killed || err.code === 'ETIMEDOUT') {
return {
exitCode: 124,
stdout: err.stdout || '',
stderr: `Execution timed out after ${timeout / 1000} seconds`,
};
}
return {
exitCode: 1,
stdout: err.stdout || '',
stderr: err.stderr || String(error),
};
}
finally {
// Cleanup temp file
try {
await unlink(tempFile);
}
catch {
// Ignore cleanup errors
}
}
}
/**
* Save blocked code to temp directory for inspection
*/
export async function saveBlockedCode(code, language) {
const ext = EXTENSIONS[language?.toLowerCase()] || '.txt';
const tempDir = await ensureTempDir();
const filename = `glm_blocked_${getTimestamp()}${ext}`;
const filepath = join(tempDir, filename);
await writeFile(filepath, code, 'utf-8');
return filepath;
}
/**
* Security Check Module
* Validates generated code for dangerous patterns before execution
*/
import type { SecurityCheckResult } from './types.js';
/**
* Check code for security violations
*/
export declare function checkCodeSecurity(code: string): SecurityCheckResult;
/**
* Format security check result for console output
*/
export declare function formatSecurityReport(result: SecurityCheckResult): string;
/**
* Security Check Module
* Validates generated code for dangerous patterns before execution
*/
/**
* Build dangerous patterns dynamically to avoid static detection
*/
function buildDangerousPatterns() {
const patterns = [];
// System command execution patterns
const sysExecMethods = ['system', 'popen'];
for (const method of sysExecMethods) {
patterns.push({
pattern: `os.${method}(`,
reason: 'System command execution',
});
}
// Subprocess patterns
const subprocessMethods = ['run', 'call', 'Popen'];
for (const method of subprocessMethods) {
patterns.push({
pattern: `subprocess.${method}(`,
reason: 'Subprocess execution',
});
}
// File deletion patterns
const osDeleteMethods = ['remove', 'unlink', 'rmdir', 'removedirs'];
for (const method of osDeleteMethods) {
patterns.push({
pattern: `os.${method}(`,
reason: 'File/directory deletion',
});
}
patterns.push({
pattern: 'shutil.rmtree(',
reason: 'Directory deletion',
});
// Dynamic import
patterns.push({
pattern: '__import__(',
reason: 'Dynamic module import',
});
// Environment manipulation
patterns.push({
pattern: 'os.environ[',
reason: 'Environment variable access',
});
patterns.push({
pattern: 'os.putenv(',
reason: 'Environment variable modification',
});
return patterns;
}
/**
* Dynamic execution function names to detect
*/
const DYNAMIC_EXEC_FUNCTIONS = ['eval', 'exec'];
/**
* Dangerous module imports to detect
*/
const DANGEROUS_IMPORTS = [
'os',
'subprocess',
'shutil',
'sys',
'commands',
'pty',
'ctypes',
];
/**
* Check if a line is an import statement for a dangerous module
*/
function checkDangerousImport(line) {
const trimmed = line.trim();
for (const module of DANGEROUS_IMPORTS) {
const importPatterns = [
new RegExp(`^import\\s+${module}\\b`),
new RegExp(`^from\\s+${module}\\b`),
new RegExp(`^import\\s+\\w+,\\s*${module}\\b`),
];
for (const pattern of importPatterns) {
if (pattern.test(trimmed)) {
return module;
}
}
}
return null;
}
/**
* Check for dynamic execution functions
*/
function checkDynamicExecution(line) {
for (const funcName of DYNAMIC_EXEC_FUNCTIONS) {
const pattern = new RegExp(`\\b${funcName}\\s*\\(`);
if (pattern.test(line)) {
return funcName;
}
}
return null;
}
/**
* Check code for security violations
*/
export function checkCodeSecurity(code) {
const violations = [];
const lines = code.split('\n');
const dangerousPatterns = buildDangerousPatterns();
lines.forEach((line, index) => {
const lineNum = index + 1;
const trimmedLine = line.trim();
// Skip empty lines and comments
if (!trimmedLine || trimmedLine.startsWith('#')) {
return;
}
// Check for dangerous imports
const dangerousModule = checkDangerousImport(line);
if (dangerousModule) {
violations.push({
pattern: `import ${dangerousModule}`,
reason: `Dangerous module import (${dangerousModule})`,
line: lineNum,
code: trimmedLine,
});
}
// Check for dangerous patterns
for (const { pattern, reason } of dangerousPatterns) {
if (line.includes(pattern)) {
violations.push({
pattern,
reason,
line: lineNum,
code: trimmedLine,
});
}
}
// Check for dynamic execution functions
const dynamicFunc = checkDynamicExecution(line);
if (dynamicFunc) {
violations.push({
pattern: `${dynamicFunc}()`,
reason: 'Dynamic code execution',
line: lineNum,
code: trimmedLine,
});
}
});
return {
safe: violations.length === 0,
violations,
};
}
/**
* Format security check result for console output
*/
export function formatSecurityReport(result) {
if (result.safe) {
return '✓ Security check passed';
}
const lines = [
'⚠️ Security check failed - execution blocked',
'',
'Violations found:',
];
for (const v of result.violations) {
lines.push(` Line ${v.line}: ${v.code}`);
lines.push(` └─ ${v.reason}`);
lines.push('');
}
return lines.join('\n');
}
+99
-9

@@ -7,3 +7,6 @@ /**

import { writeFile } from 'fs/promises';
import { resolve } from 'path';
import { loadConfig, debug, GlmClient, buildSystemPrompt, logUsage, logError, getErrorMessage, } from '../core/index.js';
import { checkCodeSecurity, formatSecurityReport } from '../core/securityCheck.js';
import { executeCode, saveBlockedCode, inferLanguageFromExtension } from '../core/executor.js';
/**

@@ -56,2 +59,14 @@ * Strip markdown code fences from output

}
else if (arg === '-l' || arg === '--language') {
options.language = args[++i];
}
else if (arg === '-x' || arg === '--exec') {
options.execute = true;
}
else if (arg === '--force') {
options.force = true;
}
else if (arg === '--dry-run') {
options.dryRun = true;
}
else if (arg === '--no-quality') {

@@ -86,11 +101,29 @@ options.noQuality = true;

const systemPrompt = buildSystemPrompt(options.profile, options.noQuality);
// Add execution mode instructions to prompt
let finalPrompt = prompt;
if (options.execute) {
const execInstructions = `[EXECUTION MODE]
- Output actual values directly using print() statements
- Do NOT create generators, factories, or random content generators
- The code will be executed immediately, so produce concrete output
- If asked to create content (poem, story, data), embed the actual content in the code
- No user input, no interactive prompts, no external dependencies
Task: `;
finalPrompt = execInstructions + prompt;
}
const messages = [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: prompt },
{ role: 'user', content: finalPrompt },
];
// Stream response to stdout
// Stream response (to stdout only if not saving to file and not executing)
let fullContent = '';
const suppressOutput = !!options.output || !!options.execute;
// Lower temperature for execution mode (more deterministic)
const temperature = options.execute ? 0.3 : undefined;
try {
for await (const chunk of client.generateCompletionStream({ messages })) {
process.stdout.write(chunk);
for await (const chunk of client.generateCompletionStream({ messages, temperature })) {
if (!suppressOutput) {
process.stdout.write(chunk);
}
fullContent += chunk;

@@ -112,4 +145,4 @@ }

}
// Ensure newline at end
if (!fullContent.endsWith('\n')) {
// Ensure newline at end (only when outputting to stdout)
if (!suppressOutput && !fullContent.endsWith('\n')) {
process.stdout.write('\n');

@@ -121,10 +154,67 @@ }

if (options.output) {
const absolutePath = resolve(options.output);
try {
await writeFile(options.output, cleanCode + '\n', 'utf-8');
console.error(`\nSaved to: ${options.output}`);
await writeFile(absolutePath, cleanCode + '\n', 'utf-8');
console.log(absolutePath);
}
catch (error) {
console.error(`\nFailed to save file: ${getErrorMessage(error)}`);
console.error(`Failed to save file: ${getErrorMessage(error)}`);
process.exit(1);
}
}
// Execute code if -x flag is set
if (options.execute || options.dryRun) {
// Determine language (default: python)
let language = options.language;
if (!language && options.output) {
language = inferLanguageFromExtension(options.output) || undefined;
}
if (!language) {
language = 'python'; // Default to python
}
// Security check
const securityResult = checkCodeSecurity(cleanCode);
if (!securityResult.safe && !options.force) {
// Security check failed
console.error(formatSecurityReport(securityResult));
// Save blocked code for inspection
const blockedPath = await saveBlockedCode(cleanCode, language);
console.error(`Code saved to: ${blockedPath}`);
console.error('Use --force to bypass security check (not recommended)');
process.exit(1);
}
if (options.force && !securityResult.safe) {
console.error('⚠️ WARNING: Bypassing security check with --force');
console.error('');
}
// Dry run - just show security result
if (options.dryRun) {
if (securityResult.safe) {
console.log('✓ Security check passed (dry-run, not executed)');
}
// Exit without executing
}
else {
// Actually execute the code
if (options.output) {
console.log('---');
}
const result = await executeCode(cleanCode, language);
if (result.stdout) {
process.stdout.write(result.stdout);
if (!result.stdout.endsWith('\n')) {
process.stdout.write('\n');
}
}
if (result.stderr) {
process.stderr.write(result.stderr);
if (!result.stderr.endsWith('\n')) {
process.stderr.write('\n');
}
}
if (result.exitCode !== 0) {
process.exit(result.exitCode);
}
}
}
// Log usage (streaming doesn't return token counts)

@@ -131,0 +221,0 @@ await logUsage({

@@ -18,2 +18,6 @@ /**

-p, --profile <name> Use specific profile (frontend-design, api-integration, etc)
-l, --language <lang> Language for execution (python, node, bash)
-x, --exec Execute generated code after security check
--force Bypass security check (not recommended)
--dry-run Check security without executing
--no-quality Disable quality instructions

@@ -30,2 +34,4 @@

glm --query "Validate email function" --output utils.py
glm --query "Print 1 to 10 sum" -l python -x
glm --query "Hello world" -o hello.py -x
echo "Parse JSON with error handling" | glm

@@ -32,0 +38,0 @@

@@ -97,4 +97,32 @@ /**

noQuality?: boolean;
execute?: boolean;
language?: string;
force?: boolean;
dryRun?: boolean;
}
/**
* Security violation found in generated code
*/
export interface SecurityViolation {
pattern: string;
reason: string;
line: number;
code: string;
}
/**
* Result of security check on generated code
*/
export interface SecurityCheckResult {
safe: boolean;
violations: SecurityViolation[];
}
/**
* Result of code execution
*/
export interface ExecuteResult {
exitCode: number;
stdout: string;
stderr: string;
}
/**
* Task types that profiles can handle

@@ -101,0 +129,0 @@ */

+1
-1
{
"name": "glm-coding",
"version": "0.5.0",
"version": "0.6.0",
"description": "GLM CLI - AI Code Generator with streaming output",

@@ -5,0 +5,0 @@ "type": "module",

@@ -100,2 +100,38 @@ # GLM CLI - AI Code Generator

### Execute Mode (NEW in v0.6.0)
Generate and immediately execute code with automatic security validation:
```bash
# Execute generated code (default: Python)
glm -x -q "Print sum of 1 to 100"
# Output: 5050
# Generate content directly (poem, data, etc.)
glm -x -q "Write a haiku about coding"
# Output: [actual haiku printed]
# Save and execute
glm -x -q "Hello world" -o hello.py
# Output: /path/to/hello.py
# Hello, World!
# Specify language
glm -x -l node -q "console.log('Hello')"
# Security check only (dry-run)
glm --dry-run -q "System information script"
# Force execution (bypass security - not recommended)
glm -x --force -q "some dangerous code"
```
**Security Features:**
- Automatic validation before execution
- Blocks dangerous imports: os, subprocess, shutil, etc.
- Blocks dangerous operations: file deletion, system commands
- Blocks dynamic code execution functions
- Detailed violation reports with line numbers
- Blocked code saved to temp directory for inspection
### Available Profiles

@@ -115,4 +151,8 @@

-q, --query <prompt> Query prompt (required if no pipe)
-o, --output <file> Save output to file
-o, --output <file> Save output to file (suppresses code output)
-p, --profile <name> Use specific profile
-l, --language <lang> Language for execution (python, node, bash)
-x, --exec Execute generated code after security check
--force Bypass security check (not recommended)
--dry-run Security check only, no execution
--no-quality Disable quality instructions

@@ -119,0 +159,0 @@ ```