Fix bin/publish: use correct .env path for rspade_system Fix bin/publish script: prevent grep exit code 1 from terminating script 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
146 lines
6.4 KiB
PHP
Executable File
146 lines
6.4 KiB
PHP
Executable File
<?php
|
|
|
|
namespace App\RSpade\CodeQuality\Rules\JavaScript;
|
|
|
|
use App\RSpade\CodeQuality\Rules\CodeQualityRule_Abstract;
|
|
use App\RSpade\CodeQuality\Support\FileSanitizer;
|
|
|
|
class DefensiveCoding_CodeQualityRule extends CodeQualityRule_Abstract
|
|
{
|
|
public function get_id(): string
|
|
{
|
|
return 'JS-DEFENSIVE-01';
|
|
}
|
|
|
|
public function get_name(): string
|
|
{
|
|
return 'JavaScript Defensive Coding Check';
|
|
}
|
|
|
|
public function get_description(): string
|
|
{
|
|
return 'Prohibits existence checks - code must fail loudly if dependencies are missing';
|
|
}
|
|
|
|
public function get_file_patterns(): array
|
|
{
|
|
return ['*.js'];
|
|
}
|
|
|
|
public function get_default_severity(): string
|
|
{
|
|
return 'high';
|
|
}
|
|
|
|
/**
|
|
* Check JavaScript file for defensive coding violations (from line 833)
|
|
*/
|
|
public function check(string $file_path, string $contents, array $metadata = []): void
|
|
{
|
|
// Skip vendor and node_modules directories
|
|
if (str_contains($file_path, '/vendor/') || str_contains($file_path, '/node_modules/')) {
|
|
return;
|
|
}
|
|
|
|
// Skip CodeQuality directory
|
|
if (str_contains($file_path, '/CodeQuality/')) {
|
|
return;
|
|
}
|
|
|
|
// Get sanitized content
|
|
$sanitized_data = FileSanitizer::sanitize_javascript($file_path);
|
|
$lines = $sanitized_data['lines'];
|
|
|
|
foreach ($lines as $line_num => $line) {
|
|
$line_number = $line_num + 1;
|
|
|
|
// Skip comments
|
|
$trimmed_line = trim($line);
|
|
if (str_starts_with($trimmed_line, '//') || str_starts_with($trimmed_line, '*')) {
|
|
continue;
|
|
}
|
|
|
|
// Pattern 1: typeof variable checks (!== undefined, === undefined, == 'function', etc.)
|
|
// Match: typeof SomeVar !== 'undefined' or typeof SomeVar == 'function'
|
|
if (preg_match('/typeof\s+(\w+)\s*([!=]=+)\s*[\'"]?(undefined|function)[\'"]?/i', $line, $matches)) {
|
|
$variable = $matches[1];
|
|
|
|
// Skip if it's a property check (contains dot)
|
|
if (!str_contains($variable, '.')) {
|
|
$this->add_violation(
|
|
$file_path,
|
|
$line_number,
|
|
"Defensive coding violation: Checking if '{$variable}' exists. All classes and variables must be assumed to exist. Code should fail loudly if something is undefined.",
|
|
trim($line),
|
|
"Remove the existence check. Let the code fail if '{$variable}' is not defined.",
|
|
'high'
|
|
);
|
|
}
|
|
}
|
|
|
|
// Pattern 2: typeof window.variable checks
|
|
if (preg_match('/typeof\s+window\.(\w+)\s*([!=]=+)\s*[\'"]?undefined[\'"]?/i', $line, $matches)) {
|
|
$variable = 'window.' . $matches[1];
|
|
$this->add_violation(
|
|
$file_path,
|
|
$line_number,
|
|
"Defensive coding violation: Checking if '{$variable}' exists. All global variables must be assumed to exist. Code should fail loudly if something is undefined.",
|
|
trim($line),
|
|
"Remove the existence check. Let the code fail if '{$variable}' is not defined.",
|
|
'high'
|
|
);
|
|
}
|
|
|
|
// Pattern 3: if (variable) or if (!variable) existence checks (more careful pattern)
|
|
// Only match simple variables, not property access
|
|
if (preg_match('/if\s*\(\s*(!)?(\w+)\s*\)/', $line, $matches)) {
|
|
$variable = $matches[2];
|
|
|
|
// Skip if it's a property or array access or a boolean-like variable name
|
|
if (!str_contains($line, '.' . $variable) &&
|
|
!str_contains($line, '[' . $variable) &&
|
|
!str_contains($line, $variable . '.') &&
|
|
!str_contains($line, $variable . '[') &&
|
|
!preg_match('/^(is|has|can|should|will|did|was)[A-Z]/', $variable) && // Skip boolean-named vars
|
|
!in_array(strtolower($variable), ['true', 'false', 'null', 'undefined'])) { // Skip literals
|
|
|
|
// Check if this looks like an existence check by looking at context
|
|
if (preg_match('/if\s*\(\s*(!)?typeof\s+' . preg_quote($variable, '/') . '/i', $line) ||
|
|
preg_match('/if\s*\(\s*' . preg_quote($variable, '/') . '\s*&&\s*' . preg_quote($variable, '/') . '\./i', $line)) {
|
|
$this->add_violation(
|
|
$file_path,
|
|
$line_number,
|
|
"Defensive coding violation: Checking if '{$variable}' exists. All classes and variables must be assumed to exist. Code should fail loudly if something is undefined.",
|
|
trim($line),
|
|
"Remove the existence check. Let the code fail if '{$variable}' is not defined.",
|
|
'high'
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pattern 4: Guard clauses like: Rsx && Rsx.method()
|
|
if (preg_match('/(\w+)\s*&&\s*\1\.\w+/i', $line, $matches)) {
|
|
$variable = $matches[1];
|
|
|
|
// Skip common boolean variable patterns
|
|
if (!preg_match('/^(is|has|can|should|will|did|was)[A-Z]/', $variable)) {
|
|
$this->add_violation(
|
|
$file_path,
|
|
$line_number,
|
|
"Defensive coding violation: Guard clause checking if '{$variable}' exists. All classes and variables must be assumed to exist. Code should fail loudly if something is undefined.",
|
|
trim($line),
|
|
"Remove the guard clause. Use '{$variable}.method()' directly.",
|
|
'high'
|
|
);
|
|
}
|
|
}
|
|
|
|
// Pattern 5: try/catch used for existence checking (simplified detection)
|
|
if (preg_match('/try\s*\{.*?(\w+).*?\}\s*catch/i', $line, $matches)) {
|
|
// This is a simplified check - in reality you'd need multi-line parsing
|
|
// Skip for now as it's complex to detect intent
|
|
}
|
|
}
|
|
}
|
|
} |