A Laravel News article published on September 17, 2026, examines Laravel Scalpel, a package by Harry Agustiana. It runs within a Laravel application and looks for filesystem evidence of an intrusion, including rogue PHP files, hidden code, altered server directives, and changes made after deployment.

terminal
php artisan scalpel:scan
php artisan scalpel:scan --only=structural,obfuscated
php artisan scalpel:scan --only=userini

Scalpel addresses a different problem from Ward and Checkpoint. Those tools inspect application code, configuration, and dependencies for security weaknesses. Scalpel searches for files that may have been added, changed, or deleted in an already deployed application.

The default scalpel:scan command uses six scanners. Five inspect the current filesystem, while Baseline Diff compares it with a saved snapshot. The structural scanner checks public/ and storage/ for executable PHP files, including .php, .phtml, .pht, .phar, and names such as shell.php.jpg. It permits public/index.php, public/vendor/, and Laravel's compiled views and cache by default. Scan paths and allow lists can be changed in the configuration.

The obfuscated-code scanner looks for patterns such as eval(base64_decode(...)), compressed payload execution, dynamic function calls, direct evaluation of request data, and unusually long encoded strings. Individual patterns can be disabled in config/scalpel.php when application code triggers a legitimate finding. The .htaccess scanner reports script handler and MIME mappings for Python, Perl, CGI, and other interpreters. It also checks Options +ExecCGI, external URL rewrites, and directives such as auto_prepend_file. The .user.ini scanner covers auto_prepend_file, auto_append_file, include_path, and disable_functions. A malicious auto_prepend_file can execute a hidden file on every request.

Environment checks cover missing, empty, or unreadable .env files, .env files under public/, and world-readable permissions on Unix-like systems. Scalpel reports an empty APP_KEY and compares .env keys with .env.example. Missing or unexpected keys are treated as possible intrusion evidence. With APP_ENV set to production, APP_DEBUG=true is reported. The --production option enables that check regardless of APP_ENV and also reports APP_ENV=local.

The package provides scalpel:scan with --only selections for individual scanners. Baselines are created with scalpel:baseline. Each included file receives a SHA-256 hash, size, and modification time. scalpel:diff reports added, modified, and deleted files. A baseline should be created only after the application has reached a trusted state. Before that, scalpel:scan and scalpel:diff produce a MEDIUM finding requesting one. After deployment, the documented sequence is php artisan optimize followed by scalpel:baseline --force.

Default exclusions cover changing paths such as logs, sessions, compiled views, and storage/app. vendor/ is skipped by content scanners but remains in baseline comparisons, so newly added package files can appear in a diff. Strict mode hashes every file during comparisons. The --fast option reuses a previous hash when file size and modification time are unchanged, which can miss an attacker-preserved change. Content-based or location-based scanners may still detect it.

Baselines and JSON reports can receive HMAC signatures when SCALPEL_SIGNING_ENABLED is true and SCALPEL_SIGNING_KEY contains a dedicated key separate from APP_KEY. scalpel:diff validates a signed baseline and marks a missing or invalid signature as CRITICAL. Signing cannot secure the baseline if an attacker can access the key or modify the scanner. It should be enabled before the first baseline. scalpel:verify checks a saved JSON report.

The article tested Scalpel 1.9.0 in a new Laravel 13.31 application. With .env permissions set to 0600 and a baseline available, the default scan produced no findings. Four harmless test fixtures then introduced eval(base64_decode(...)) inside if (false), a file named avatar.php.jpg, an .htaccess handler mapping, and a .user.ini file containing auto_prepend_file. The four corresponding scanners were run with php artisan scalpel:scan --only=structural,obfuscated,htaccess,userini --no-banner. A complete scan produced nine findings because Baseline Diff also saw those four files and a modified routes/web.php.

The test exposed a CI issue with generated views. Running php artisan optimize created compiled framework views in storage/framework/views. The next scan reported 100 MEDIUM variable-variable findings and two HIGH backtick findings there. php artisan optimize:clear followed by a new baseline restored a clean result. Structural scanning permits the compiled-view directory, but obfuscated-code scanning still reads it. Teams caching views in production should test this path before using Scalpel as a deployment gate. Adding storage/framework/views to content_scan_excluded_paths removes these findings while excluding all content checks for compiled views.

scan and diff support table, JSON, GitHub Actions annotation, and SARIF output. --fail-on sets the lowest severity that fails CI, with HIGH as the default. The documented SARIF example is php artisan scalpel:scan --format=sarif --fail-on=MEDIUM. Exit code 0 means no finding was returned, code 1 means a finding met or exceeded the threshold, and code 2 indicates only lower-severity findings or an incomplete scan such as an unreadable directory.

After either command, Scalpel dispatches ScanFinished. The event contains the findings, the command name, scan or diff, and the duration in milliseconds. Applications can use it for mail, Slack, or webhook alerts without parsing terminal output.

The package requires PHP 8.2 or later and supports Laravel 10 through 13. Installation uses composer require hryagstn/laravel-scalpel, followed by php artisan vendor:publish --tag=scalpel-config. Scalpel shares the application's process and filesystem permissions. An attacker who can modify application code may also modify the scanner or its configuration. The project recommends external triggers, read-only code directories, and sending results outside the potentially compromised server. Scalpel identifies intrusion evidence and provides no firewall or containment function. The source and command reference are available on GitHub, with a command simulator on the Laravel Scalpel website.