Ban proc_open() and exec() entirely - replace with shell_exec() and file redirection

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-10-21 05:25:33 +00:00
parent 94c68861cc
commit 1c561dd301
5 changed files with 192 additions and 357 deletions

View File

@@ -19,7 +19,7 @@ class StreamBlockingMode_CodeQualityRule extends CodeQualityRule_Abstract
public function get_description(): string
{
return 'Streams from proc_open() and network sockets must set blocking mode before reading to prevent data truncation';
return 'Streams from fsockopen() and network sockets must set blocking mode before reading to prevent data truncation (note: proc_open() is banned - see PHP-PROC-01)';
}
public function get_file_patterns(): array
@@ -36,9 +36,11 @@ class StreamBlockingMode_CodeQualityRule extends CodeQualityRule_Abstract
* Check PHP file for stream operations without explicit blocking mode
* and dangerous read patterns that truncate data
*
* PHP streams from proc_open(), fsockopen(), popen(), and stream_socket_client()
* PHP streams from fsockopen(), popen(), and stream_socket_client()
* default to non-blocking mode in some contexts, which causes incomplete reads
* and silent data truncation.
*
* Note: proc_open() is completely banned by PHP-PROC-01 rule, so it's excluded here.
*/
public function check(string $file_path, string $contents, array $metadata = []): void
{
@@ -56,8 +58,8 @@ class StreamBlockingMode_CodeQualityRule extends CodeQualityRule_Abstract
$sanitized_data = FileSanitizer::sanitize_php($contents);
$sanitized_code = $sanitized_data['content'];
// Check for stream sources
$stream_sources_pattern = '/\b(proc_open|fsockopen|stream_socket_client|popen)\s*\(/i';
// Check for stream sources (proc_open excluded - it's banned by PHP-PROC-01)
$stream_sources_pattern = '/\b(fsockopen|stream_socket_client|popen)\s*\(/i';
if (!preg_match($stream_sources_pattern, $sanitized_code)) {
return; // No stream sources, skip
}
@@ -140,14 +142,16 @@ class StreamBlockingMode_CodeQualityRule extends CodeQualityRule_Abstract
{
return "Stream operations without explicit blocking mode
File uses stream sources (proc_open/fsockopen/popen/stream_socket_client) with
File uses stream sources (fsockopen/popen/stream_socket_client) with
read operations but does not call stream_set_blocking().
Note: proc_open() is banned entirely - see PHP-PROC-01 rule.
PHP streams default to non-blocking mode in some contexts, which causes:
- Incomplete reads with partial data
- Silent data truncation (no errors or warnings)
- Race conditions depending on when data arrives
- Data integrity issues for command output and file transfers";
- Data integrity issues for network transfers and command output";
}
private function get_missing_blocking_resolution(): string
@@ -156,30 +160,23 @@ PHP streams default to non-blocking mode in some contexts, which causes:
Add stream_set_blocking(\$stream, true) before reading AND use proper read loop:
CORRECT PATTERN:
\$process = proc_open(\$command, \$descriptors, \$pipes);
fclose(\$pipes[0]);
CORRECT PATTERN (for network sockets):
\$socket = fsockopen('example.com', 80);
stream_set_blocking(\$pipes[1], true);
stream_set_blocking(\$pipes[2], true);
stream_set_blocking(\$socket, true);
// Read stdout until EOF
// Read until EOF
\$output = '';
while (!feof(\$pipes[1])) {
\$chunk = fread(\$pipes[1], 8192);
while (!feof(\$socket)) {
\$chunk = fread(\$socket, 8192);
if (\$chunk !== false) {
\$output .= \$chunk;
}
}
// Read stderr until EOF
\$error = '';
while (!feof(\$pipes[2])) {
\$chunk = fread(\$pipes[2], 8192);
if (\$chunk !== false) {
\$error .= \$chunk;
}
}";
fclose(\$socket);
Note: If you're using proc_open(), replace it with file redirection (see PHP-PROC-01).";
}
private function get_dangerous_break_message(): string