Add comprehensive security audit (86 findings across 10 areas)
Secure dev auth with signed tokens, add email support for --user Simplify breakpoint variables, suppress Sass deprecation warnings 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,8 @@ use App\RSpade\Core\Ajax\Exceptions\AjaxFormErrorException;
|
|||||||
use App\RSpade\Core\Ajax\Exceptions\AjaxFatalErrorException;
|
use App\RSpade\Core\Ajax\Exceptions\AjaxFatalErrorException;
|
||||||
use App\RSpade\Core\Session\Session;
|
use App\RSpade\Core\Session\Session;
|
||||||
use App\RSpade\Core\Debug\Debugger;
|
use App\RSpade\Core\Debug\Debugger;
|
||||||
|
use App\RSpade\Core\Models\Login_User_Model;
|
||||||
|
use App\RSpade\Core\Models\Site_Model;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RSX Ajax Command
|
* RSX Ajax Command
|
||||||
@@ -32,10 +34,11 @@ use App\RSpade\Core\Debug\Debugger;
|
|||||||
* USAGE EXAMPLES:
|
* USAGE EXAMPLES:
|
||||||
* php artisan rsx:ajax Controller action # Basic call
|
* php artisan rsx:ajax Controller action # Basic call
|
||||||
* php artisan rsx:ajax Controller action --args='{"id":1}' # With params
|
* php artisan rsx:ajax Controller action --args='{"id":1}' # With params
|
||||||
* php artisan rsx:ajax Controller action --site-id=1 # With site context
|
* php artisan rsx:ajax Controller action --site=1 # With site context
|
||||||
* php artisan rsx:ajax Controller action --user-id=1 --site-id=1 # Full context
|
* php artisan rsx:ajax Controller action --user=1 --site=1 # Full context
|
||||||
|
* php artisan rsx:ajax Controller action --user=admin@test.com # User by email
|
||||||
* php artisan rsx:ajax Controller action --debug # HTTP-like response
|
* php artisan rsx:ajax Controller action --debug # HTTP-like response
|
||||||
* php artisan rsx:ajax Controller action --verbose --site-id=1 # Show context
|
* php artisan rsx:ajax Controller action --verbose --site=1 # Show context
|
||||||
*
|
*
|
||||||
* OUTPUT FORMAT:
|
* OUTPUT FORMAT:
|
||||||
* Default: {"records":[...], "total":10}
|
* Default: {"records":[...], "total":10}
|
||||||
@@ -62,8 +65,8 @@ class Ajax_Debug_Command extends Command
|
|||||||
{controller : The RSX controller name}
|
{controller : The RSX controller name}
|
||||||
{action : The action/method name}
|
{action : The action/method name}
|
||||||
{--args= : JSON-encoded arguments to pass to the action}
|
{--args= : JSON-encoded arguments to pass to the action}
|
||||||
{--user-id= : Set user ID for session context}
|
{--user= : Set user ID or email for session context}
|
||||||
{--site-id= : Set site ID for session context}
|
{--site= : Set site ID for session context}
|
||||||
{--debug : Wrap output in HTTP-like response format (success, _ajax_return_value, console_debug)}
|
{--debug : Wrap output in HTTP-like response format (success, _ajax_return_value, console_debug)}
|
||||||
{--show-context : Show request context before JSON output}';
|
{--show-context : Show request context before JSON output}';
|
||||||
|
|
||||||
@@ -96,11 +99,29 @@ class Ajax_Debug_Command extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get options
|
// Get options
|
||||||
$user_id = $this->option('user-id');
|
$user_input = $this->option('user');
|
||||||
$site_id = $this->option('site-id');
|
$site_input = $this->option('site');
|
||||||
$debug_mode = $this->option('debug');
|
$debug_mode = $this->option('debug');
|
||||||
$show_context = $this->option('show-context');
|
$show_context = $this->option('show-context');
|
||||||
|
|
||||||
|
// Validate and resolve user
|
||||||
|
$user_id = null;
|
||||||
|
if ($user_input !== null) {
|
||||||
|
$user_id = $this->resolve_user($user_input);
|
||||||
|
if ($user_id === null) {
|
||||||
|
return 1; // Error already displayed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate site
|
||||||
|
$site_id = null;
|
||||||
|
if ($site_input !== null) {
|
||||||
|
$site_id = $this->resolve_site($site_input);
|
||||||
|
if ($site_id === null) {
|
||||||
|
return 1; // Error already displayed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Rotate logs before test
|
// Rotate logs before test
|
||||||
Debugger::logrotate();
|
Debugger::logrotate();
|
||||||
|
|
||||||
@@ -205,4 +226,66 @@ class Ajax_Debug_Command extends Command
|
|||||||
|
|
||||||
$this->output_json($error);
|
$this->output_json($error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve user identifier to user ID
|
||||||
|
*
|
||||||
|
* Accepts either a numeric user ID or an email address.
|
||||||
|
* Validates that the user exists in the database.
|
||||||
|
*
|
||||||
|
* @param string $user_input User ID or email address
|
||||||
|
* @return int|null User ID or null if not found (error already displayed)
|
||||||
|
*/
|
||||||
|
protected function resolve_user(string $user_input): ?int
|
||||||
|
{
|
||||||
|
// Check if input is an email address
|
||||||
|
if (str_contains($user_input, '@')) {
|
||||||
|
$login_user = Login_User_Model::find_by_email($user_input);
|
||||||
|
if (!$login_user) {
|
||||||
|
$this->output_json_error("User not found: {$user_input}", 'user_not_found');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return $login_user->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input is a user ID - validate it exists
|
||||||
|
if (!ctype_digit($user_input)) {
|
||||||
|
$this->output_json_error("Invalid user identifier: {$user_input} (must be numeric ID or email address)", 'invalid_user');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user_id = (int) $user_input;
|
||||||
|
$login_user = Login_User_Model::find($user_id);
|
||||||
|
if (!$login_user) {
|
||||||
|
$this->output_json_error("User ID not found: {$user_id}", 'user_not_found');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve site identifier to site ID
|
||||||
|
*
|
||||||
|
* Validates that the site exists in the database.
|
||||||
|
*
|
||||||
|
* @param string $site_input Site ID
|
||||||
|
* @return int|null Site ID or null if not found (error already displayed)
|
||||||
|
*/
|
||||||
|
protected function resolve_site(string $site_input): ?int
|
||||||
|
{
|
||||||
|
if (!ctype_digit($site_input)) {
|
||||||
|
$this->output_json_error("Invalid site identifier: {$site_input} (must be numeric ID)", 'invalid_site');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$site_id = (int) $site_input;
|
||||||
|
$site = Site_Model::find($site_id);
|
||||||
|
if (!$site) {
|
||||||
|
$this->output_json_error("Site ID not found: {$site_id}", 'site_not_found');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $site_id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ namespace App\RSpade\Commands\Rsx;
|
|||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Symfony\Component\Process\Process;
|
use Symfony\Component\Process\Process;
|
||||||
use App\RSpade\Core\Debug\Debugger;
|
use App\RSpade\Core\Debug\Debugger;
|
||||||
|
use App\RSpade\Core\Models\Login_User_Model;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RSX Route Debug Command
|
* RSX Route Debug Command
|
||||||
@@ -29,7 +30,7 @@ use App\RSpade\Core\Debug\Debugger;
|
|||||||
* 6. Rotates logs again after test for clean slate on next run
|
* 6. Rotates logs again after test for clean slate on next run
|
||||||
*
|
*
|
||||||
* KEY FEATURES:
|
* KEY FEATURES:
|
||||||
* - Backdoor authentication: Use --user-id to bypass login and test as any user
|
* - Backdoor authentication: Use --user with ID or email to bypass login
|
||||||
* - Plain text error output: Errors returned as plain text with stack traces
|
* - Plain text error output: Errors returned as plain text with stack traces
|
||||||
* - Console capture: JavaScript errors and logs captured (--console for all)
|
* - Console capture: JavaScript errors and logs captured (--console for all)
|
||||||
* - XHR/fetch tracking: Monitor API calls with --xhr-dump or --xhr-list
|
* - XHR/fetch tracking: Monitor API calls with --xhr-dump or --xhr-list
|
||||||
@@ -55,7 +56,8 @@ use App\RSpade\Core\Debug\Debugger;
|
|||||||
*
|
*
|
||||||
* USAGE EXAMPLES:
|
* USAGE EXAMPLES:
|
||||||
* php artisan rsx:debug /dashboard # Basic route test
|
* php artisan rsx:debug /dashboard # Basic route test
|
||||||
* php artisan rsx:debug /dashboard --user-id=1 # Test as user ID 1
|
* php artisan rsx:debug /dashboard --user=1 # Test as user ID 1
|
||||||
|
* php artisan rsx:debug /dashboard --user=admin@test.com # Test as user by email
|
||||||
* php artisan rsx:debug /api/users --no-body # Headers only
|
* php artisan rsx:debug /api/users --no-body # Headers only
|
||||||
* php artisan rsx:debug /login --full # Maximum information
|
* php artisan rsx:debug /login --full # Maximum information
|
||||||
* php artisan rsx:debug /api/data --xhr-list # Simple XHR list
|
* php artisan rsx:debug /api/data --xhr-list # Simple XHR list
|
||||||
@@ -127,8 +129,7 @@ class Route_Debug_Command extends Command
|
|||||||
protected $signature = 'rsx:debug
|
protected $signature = 'rsx:debug
|
||||||
{url? : The URL to debug (e.g., /dashboard, /api/users). Use --examples to see usage examples}
|
{url? : The URL to debug (e.g., /dashboard, /api/users). Use --examples to see usage examples}
|
||||||
{--examples : Show comprehensive usage examples}
|
{--examples : Show comprehensive usage examples}
|
||||||
{--user= : Test as specific user ID (bypasses authentication)}
|
{--user= : Test as specific user ID or email (bypasses authentication)}
|
||||||
{--user-id= : Alias for --user option}
|
|
||||||
{--log : Display Laravel error log if not empty}
|
{--log : Display Laravel error log if not empty}
|
||||||
{--no-body : Suppress HTTP response body (show headers/status only)}
|
{--no-body : Suppress HTTP response body (show headers/status only)}
|
||||||
{--follow-redirects : Follow HTTP redirects and show full redirect chain}
|
{--follow-redirects : Follow HTTP redirects and show full redirect chain}
|
||||||
@@ -212,8 +213,14 @@ class Route_Debug_Command extends Command
|
|||||||
$url = '/' . $url;
|
$url = '/' . $url;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user ID from options
|
// Get user ID from options (accepts ID or email)
|
||||||
$user_id = $this->option('user-id') ?: $this->option('user');
|
$user_id = $this->option('user');
|
||||||
|
if ($user_id !== null) {
|
||||||
|
$user_id = $this->resolve_user($user_id);
|
||||||
|
if ($user_id === null) {
|
||||||
|
return 1; // Error already displayed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get log flag
|
// Get log flag
|
||||||
$show_log = $this->option('log');
|
$show_log = $this->option('log');
|
||||||
@@ -373,11 +380,22 @@ class Route_Debug_Command extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate signed request token for user/site context
|
||||||
|
// This prevents unauthorized requests from hijacking sessions via headers
|
||||||
|
$dev_auth_token = null;
|
||||||
|
if ($user_id) {
|
||||||
|
$dev_auth_token = $this->generate_dev_auth_token($url, $user_id);
|
||||||
|
}
|
||||||
|
|
||||||
// Build command arguments
|
// Build command arguments
|
||||||
$command_args = ['node', $playwright_script, $url];
|
$command_args = ['node', $playwright_script, $url];
|
||||||
|
|
||||||
if ($user_id) {
|
if ($user_id) {
|
||||||
$command_args[] = "--user-id={$user_id}";
|
$command_args[] = "--user={$user_id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($dev_auth_token) {
|
||||||
|
$command_args[] = "--dev-auth-token={$dev_auth_token}";
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($show_log) {
|
if ($show_log) {
|
||||||
@@ -539,7 +557,7 @@ class Route_Debug_Command extends Command
|
|||||||
|
|
||||||
$this->comment('AUTHENTICATION:');
|
$this->comment('AUTHENTICATION:');
|
||||||
$this->line(' php artisan rsx:debug /admin --user=1 # Test as user ID 1');
|
$this->line(' php artisan rsx:debug /admin --user=1 # Test as user ID 1');
|
||||||
$this->line(' php artisan rsx:debug /profile --user-id=5 # Alternative syntax');
|
$this->line(' php artisan rsx:debug /admin --user=admin@example.com # Test as user by email');
|
||||||
$this->line('');
|
$this->line('');
|
||||||
|
|
||||||
$this->comment('TESTING RSX JAVASCRIPT:');
|
$this->comment('TESTING RSX JAVASCRIPT:');
|
||||||
@@ -619,4 +637,70 @@ class Route_Debug_Command extends Command
|
|||||||
$this->line(' • For more details on console_debug: php artisan rsx:man console_debug');
|
$this->line(' • For more details on console_debug: php artisan rsx:man console_debug');
|
||||||
$this->line(' • For config options: php artisan rsx:man config_rsx');
|
$this->line(' • For config options: php artisan rsx:man config_rsx');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve user identifier to user ID
|
||||||
|
*
|
||||||
|
* Accepts either a numeric user ID or an email address.
|
||||||
|
* Validates that the user exists in the database.
|
||||||
|
*
|
||||||
|
* @param string $user_input User ID or email address
|
||||||
|
* @return int|null User ID or null if not found (error already displayed)
|
||||||
|
*/
|
||||||
|
protected function resolve_user(string $user_input): ?int
|
||||||
|
{
|
||||||
|
// Check if input is an email address
|
||||||
|
if (str_contains($user_input, '@')) {
|
||||||
|
$login_user = Login_User_Model::find_by_email($user_input);
|
||||||
|
if (!$login_user) {
|
||||||
|
$this->error("User not found: {$user_input}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return $login_user->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input is a user ID - validate it exists
|
||||||
|
if (!ctype_digit($user_input)) {
|
||||||
|
$this->error("Invalid user identifier: {$user_input} (must be numeric ID or email address)");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user_id = (int) $user_input;
|
||||||
|
$login_user = Login_User_Model::find($user_id);
|
||||||
|
if (!$login_user) {
|
||||||
|
$this->error("User ID not found: {$user_id}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a signed dev auth token for Playwright requests
|
||||||
|
*
|
||||||
|
* The token is an HMAC signature of the request parameters using APP_KEY.
|
||||||
|
* This ensures that only requests originating from rsx:debug (which has
|
||||||
|
* access to APP_KEY) can authenticate as different users.
|
||||||
|
*
|
||||||
|
* @param string $url The URL being tested
|
||||||
|
* @param int $user_id The user ID to authenticate as
|
||||||
|
* @return string The signed token
|
||||||
|
*/
|
||||||
|
protected function generate_dev_auth_token(string $url, int $user_id): string
|
||||||
|
{
|
||||||
|
$app_key = config('app.key');
|
||||||
|
if (!$app_key) {
|
||||||
|
$this->error("APP_KEY not configured - cannot generate dev auth token");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create payload with request parameters
|
||||||
|
$payload = json_encode([
|
||||||
|
'url' => $url,
|
||||||
|
'user_id' => $user_id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Sign with HMAC-SHA256
|
||||||
|
return hash_hmac('sha256', $payload, $app_key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ class Responsive {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styles = getComputedStyle(document.documentElement);
|
const styles = getComputedStyle(document.documentElement);
|
||||||
const value = styles.getPropertyValue(`--${name}`).trim();
|
const value = styles.getPropertyValue(`--bp-${name}`).trim();
|
||||||
const parsed = parseInt(value, 10);
|
const parsed = parseInt(value, 10);
|
||||||
|
|
||||||
this._cache[name] = parsed;
|
this._cache[name] = parsed;
|
||||||
@@ -84,7 +84,7 @@ class Responsive {
|
|||||||
*/
|
*/
|
||||||
static is_desktop_sm() {
|
static is_desktop_sm() {
|
||||||
const vp = this._viewport();
|
const vp = this._viewport();
|
||||||
return vp >= this._get_breakpoint('desktop-sm') && vp < this._get_breakpoint('desktop-md');
|
return vp >= this._get_breakpoint('desktop') && vp < this._get_breakpoint('desktop-md');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -352,6 +352,7 @@ async function compile() {
|
|||||||
sourceMap: enableSourceMaps,
|
sourceMap: enableSourceMaps,
|
||||||
sourceMapIncludeSources: true,
|
sourceMapIncludeSources: true,
|
||||||
verbose: !isProduction, // Show all deprecation warnings in dev mode
|
verbose: !isProduction, // Show all deprecation warnings in dev mode
|
||||||
|
silenceDeprecations: ['import'], // Suppress @import deprecation warnings until Sass 3.0 migration
|
||||||
loadPaths: [
|
loadPaths: [
|
||||||
path.dirname(inputFile),
|
path.dirname(inputFile),
|
||||||
basePath + '/rsx',
|
basePath + '/rsx',
|
||||||
|
|||||||
@@ -21,9 +21,11 @@ Examples:
|
|||||||
|
|
||||||
CORE OPTIONS
|
CORE OPTIONS
|
||||||
|
|
||||||
--user=<id> | --user-id=<id>
|
--user=<id|email>
|
||||||
Test as a specific user ID, bypassing authentication. Uses backdoor
|
Test as a specific user, bypassing authentication. Accepts either a
|
||||||
authentication that only works in development environments.
|
numeric user ID or an email address. Validates user exists before
|
||||||
|
running test. Uses backdoor authentication that only works in
|
||||||
|
development environments.
|
||||||
|
|
||||||
--no-body
|
--no-body
|
||||||
Suppress HTTP response body output. Useful when you only want to see
|
Suppress HTTP response body output. Useful when you only want to see
|
||||||
@@ -201,14 +203,24 @@ JAVASCRIPT EVALUATION
|
|||||||
|
|
||||||
AUTHENTICATION & BACKDOOR
|
AUTHENTICATION & BACKDOOR
|
||||||
|
|
||||||
The --user and --user-id options use backdoor authentication that only works
|
The --user option accepts either a numeric user ID or email address. When
|
||||||
in development/testing environments. The tool sends an X-Dev-Auth-User-Id
|
an email is provided, it is resolved to the user ID before testing. The
|
||||||
header that the framework recognizes and uses to authenticate as that user
|
user must exist in the database or the command will fail with an error.
|
||||||
without requiring login credentials.
|
|
||||||
|
SECURITY: Authentication is protected by a signed token (HMAC-SHA256) using
|
||||||
|
the application's APP_KEY. The token is generated by rsx:debug and verified
|
||||||
|
by the framework before any user override occurs. This prevents:
|
||||||
|
|
||||||
|
- External requests from hijacking sessions by sending headers directly
|
||||||
|
- Attackers from authenticating as arbitrary users even in development
|
||||||
|
|
||||||
|
The framework silently ignores authentication headers without a valid token.
|
||||||
|
Raw curl requests with X-Dev-Auth-User-Id will NOT authenticate.
|
||||||
|
|
||||||
This feature is:
|
This feature is:
|
||||||
- Only available in local/development/testing environments
|
- Only available in local/development/testing environments
|
||||||
- Automatically disabled in production
|
- Requires valid signed token (generated from APP_KEY)
|
||||||
|
- Automatically disabled in production (APP_KEY is cleared in .env.dist)
|
||||||
- Useful for testing protected routes
|
- Useful for testing protected routes
|
||||||
- Does not require modifying session state
|
- Does not require modifying session state
|
||||||
|
|
||||||
@@ -251,6 +263,9 @@ COMMON PATTERNS
|
|||||||
Test a protected route as user ID 1:
|
Test a protected route as user ID 1:
|
||||||
php artisan rsx:debug /admin/users --user=1
|
php artisan rsx:debug /admin/users --user=1
|
||||||
|
|
||||||
|
Test a protected route by email:
|
||||||
|
php artisan rsx:debug /admin/users --user=admin@example.com
|
||||||
|
|
||||||
Check if JavaScript errors occur:
|
Check if JavaScript errors occur:
|
||||||
php artisan rsx:debug /page
|
php artisan rsx:debug /page
|
||||||
# Console errors are always shown
|
# Console errors are always shown
|
||||||
|
|||||||
46
app/RSpade/upstream_changes/claude_question_guard_12_18.txt
Executable file
46
app/RSpade/upstream_changes/claude_question_guard_12_18.txt
Executable file
@@ -0,0 +1,46 @@
|
|||||||
|
Claude Code Hook Implementation
|
||||||
|
|
||||||
|
Instructions (for applying to other projects)
|
||||||
|
|
||||||
|
1. Create the hook script at .claude/hooks/question-guard.sh:
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Read JSON input from stdin and extract the prompt field
|
||||||
|
prompt=$(cat | jq -r '.prompt // empty')
|
||||||
|
|
||||||
|
# Check if the prompt ends with a question mark (ignoring trailing whitespace)
|
||||||
|
if echo "$prompt" | grep -qE '\?\s*$'; then
|
||||||
|
# Use JSON additionalContext for discrete injection (not shown to user)
|
||||||
|
cat <<'EOF'
|
||||||
|
{
|
||||||
|
"hookSpecificOutput": {
|
||||||
|
"hookEventName": "UserPromptSubmit",
|
||||||
|
"additionalContext": "STOP: This is a QUESTION. Suspend the current task, if any, and answer the question directly and concisely. Do NOT take any further actions, run any tools (except as necessary to answer the question), or continue previous work until instructed to 'resume' or otherwise continue the task. Wait for the user's next instruction."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
2. Make it executable:
|
||||||
|
chmod +x .claude/hooks/question-guard.sh
|
||||||
|
|
||||||
|
3. Add to .claude/settings.json (or .claude/settings.local.json for local-only):
|
||||||
|
{
|
||||||
|
"hooks": {
|
||||||
|
"UserPromptSubmit": [
|
||||||
|
{
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": ".claude/hooks/question-guard.sh"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
|
||||||
|
- jq must be installed on the system
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
* route The route to debug (e.g., /dashboard)
|
* route The route to debug (e.g., /dashboard)
|
||||||
*
|
*
|
||||||
* Options:
|
* Options:
|
||||||
* --user-id=<id> Test as specific user ID
|
* --user=<id> Test as specific user ID
|
||||||
* --log Always display Laravel error log
|
* --log Always display Laravel error log
|
||||||
* --no-body Suppress body output
|
* --no-body Suppress body output
|
||||||
* --follow-redirects Follow redirects and show redirect chain
|
* --follow-redirects Follow redirects and show redirect chain
|
||||||
@@ -33,7 +33,7 @@ function parse_args() {
|
|||||||
console.log(' route The route to debug (e.g., /dashboard)');
|
console.log(' route The route to debug (e.g., /dashboard)');
|
||||||
console.log('');
|
console.log('');
|
||||||
console.log('Options:');
|
console.log('Options:');
|
||||||
console.log(' --user-id=<id> Test as specific user ID');
|
console.log(' --user=<id> Test as specific user ID');
|
||||||
console.log(' --log Always display Laravel error log');
|
console.log(' --log Always display Laravel error log');
|
||||||
console.log(' --no-body Suppress body output');
|
console.log(' --no-body Suppress body output');
|
||||||
console.log(' --follow-redirects Follow redirects and show redirect chain');
|
console.log(' --follow-redirects Follow redirects and show redirect chain');
|
||||||
@@ -98,12 +98,15 @@ function parse_args() {
|
|||||||
console_debug_disable: false,
|
console_debug_disable: false,
|
||||||
screenshot_width: null,
|
screenshot_width: null,
|
||||||
screenshot_path: null,
|
screenshot_path: null,
|
||||||
dump_dimensions: null
|
dump_dimensions: null,
|
||||||
|
dev_auth_token: null
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const arg of args) {
|
for (const arg of args) {
|
||||||
if (arg.startsWith('--user-id=')) {
|
if (arg.startsWith('--user=')) {
|
||||||
options.user_id = arg.split('=')[1];
|
options.user_id = arg.split('=')[1];
|
||||||
|
} else if (arg.startsWith('--dev-auth-token=')) {
|
||||||
|
options.dev_auth_token = arg.split('=')[1];
|
||||||
} else if (arg === '--log') {
|
} else if (arg === '--log') {
|
||||||
options.show_log = true;
|
options.show_log = true;
|
||||||
} else if (arg === '--no-body') {
|
} else if (arg === '--no-body') {
|
||||||
@@ -377,6 +380,9 @@ function parse_args() {
|
|||||||
if (options.user_id) {
|
if (options.user_id) {
|
||||||
extraHeaders['X-Dev-Auth-User-Id'] = options.user_id;
|
extraHeaders['X-Dev-Auth-User-Id'] = options.user_id;
|
||||||
}
|
}
|
||||||
|
if (options.dev_auth_token) {
|
||||||
|
extraHeaders['X-Dev-Auth-Token'] = options.dev_auth_token;
|
||||||
|
}
|
||||||
// Add Playwright test header to get text errors
|
// Add Playwright test header to get text errors
|
||||||
extraHeaders['X-Playwright-Test'] = '1';
|
extraHeaders['X-Playwright-Test'] = '1';
|
||||||
// Add console debug header if console logging is requested
|
// Add console debug header if console logging is requested
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
"postcss": "^8.1.14",
|
"postcss": "^8.1.14",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.0",
|
||||||
"resolve-url-loader": "^5.0.0",
|
"resolve-url-loader": "^5.0.0",
|
||||||
"sass": "^1.87.0",
|
"sass": ">=1.87.0 <3.0.0",
|
||||||
"sass-loader": "^12.6.0",
|
"sass-loader": "^12.6.0",
|
||||||
"select2": "^4.1.0-rc.0"
|
"select2": "^4.1.0-rc.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ The manifest may not be properly registering stub files during initial CLI build
|
|||||||
php artisan rsx:dev:update_npm
|
php artisan rsx:dev:update_npm
|
||||||
|
|
||||||
# Test (will show error)
|
# Test (will show error)
|
||||||
php artisan rsx:debug /contacts --user-id=1
|
php artisan rsx:debug /contacts --user=1
|
||||||
|
|
||||||
# Expected error: Frontend_Clients_Controller is not defined
|
# Expected error: Frontend_Clients_Controller is not defined
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
## How Created
|
## How Created
|
||||||
1. Ran `php artisan rsx:clean` (cleared all caches)
|
1. Ran `php artisan rsx:clean` (cleared all caches)
|
||||||
2. Ran `php artisan rsx:debug /contacts --user-id=1`
|
2. Ran `php artisan rsx:debug /contacts --user=1`
|
||||||
3. Browser-triggered rebuild detected cache was empty
|
3. Browser-triggered rebuild detected cache was empty
|
||||||
4. Manifest regenerated and bundles recompiled by browser request
|
4. Manifest regenerated and bundles recompiled by browser request
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ Compare this snapshot with `/storage-broken/` to identify:
|
|||||||
php artisan rsx:clean
|
php artisan rsx:clean
|
||||||
|
|
||||||
# Test (will trigger browser rebuild and work correctly)
|
# Test (will trigger browser rebuild and work correctly)
|
||||||
php artisan rsx:debug /contacts --user-id=1
|
php artisan rsx:debug /contacts --user=1
|
||||||
|
|
||||||
# Expected result: No JavaScript errors
|
# Expected result: No JavaScript errors
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user