Conversation
Rework the package around the new CI-focused audit flow: add typed audit/report value objects, replace the old cache/schedule/history model, introduce baseline and new reporter commands, tighten notifications and suppressions, and update the package metadata/docs for Laravel 12–13 support.
- Introduced new audit types: `platform` and `source`, enhancing security checks. - Added `nikic/php-parser` and `symfony/finder` as dependencies in composer.json. - Updated README to reflect new initialization command and audit options. - Enhanced audit reporting to include blocking and advisory counts. - Improved configuration options for source paths and supply chain review. - Added tests for new functionalities and ensured existing tests cover recent changes.
Revamp console output and polish docs. Updates: - Reworked ConsoleReporter to produce a rich, grouped, and colorized summary (durations, per-check counts, severity breakdowns, locations, remediation, etc.) with helper methods for grouping, wrapping, and formatting. - ReportFormatterFactory now accepts a decorated flag and passes it to ConsoleReporter. - WardenAuditCommand improved progress lines, escapes output, and forwards decoration state when building formatters. - README.md and UPGRADE.md refreshed with badges, intro, and clearer sections. - Tests updated (ReporterTest) to cover the new console grouping and per-check summaries. These changes improve CLI readability and ensure ANSI decoration is handled correctly.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (27)
💤 Files with no reviewable changes (1)
🚧 Files skipped from review as they are similar to previous changes (18)
📝 WalkthroughWalkthroughWarden 2.0 replaces stateful audits with typed results, policy-based filtering, structured report formats, new audit and initialisation commands, source and supply-chain analysis, suppression/baseline handling, and expanded CI and documentation. ChangesWarden 2.0 audit platform
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR delivers the Warden 2.0 overhaul, refocusing the package into a deterministic, CI-first security gate for Laravel apps with typed audit/report contracts, strict exit codes, new audit coverage (source + platform + supply-chain), and multiple machine-readable report formats.
Changes:
- Introduces typed value objects (
Finding,AuditResult,AuditReport,AuditError,AuditContext) plus a new execution pipeline (AuditRunner/AuditExecutor) with strict exit-code semantics. - Adds/updates core audits (source scanning, platform support calendar, supply-chain checks, Laravel config checks, storage checks) and standardized reporters (console/json/github/gitlab/sarif/junit).
- Adds safe onboarding/ops tooling and docs:
warden:init,warden:baseline, UPGRADE guide, rule catalogue, and expanded CI matrix.
Reviewed changes
Copilot reviewed 83 out of 84 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
UPGRADE.md |
Adds a 2.0 migration guide and highlights breaking CLI/config changes. |
tests/ValueObjects/AuditReportTest.php |
Verifies determinism (ordering/fingerprints) and exit-code behavior. |
tests/TestCase.php |
Introduces a shared Testbench base test case for package tests. |
tests/Services/SuppressionServiceTest.php |
Covers reviewed suppressions, expiry handling, and baseline fingerprint matching. |
tests/Services/NotificationDispatcherTest.php |
Ensures configured notification channels dispatch once. |
tests/Services/CompetitorCapabilityTest.php |
Validates the competitor capability fixture structure. |
tests/Services/Audits/SupplyChainAuditServiceTest.php |
Tests supply-chain findings (composer config, lockfiles, package-age logic). |
tests/Services/Audits/SourceAuditServiceTest.php |
Tests parser-backed source scanning, redaction, overrides, exclusions, and timeouts. |
tests/Services/Audits/PlatformAuditServiceTest.php |
Tests platform support calendar findings and blocking/advisory behavior. |
tests/Services/Audits/LaravelConfigAuditServiceTest.php |
Tests production-vs-ci config checks and expected findings. |
tests/Services/Audits/DependencyAuditServiceTest.php |
Tests composer/npm audit command construction and parsing. |
tests/Services/Audits/DebugModeAuditServiceTest.php |
Removes legacy debug-mode audit tests (feature removed/refactored). |
tests/Services/AuditExecutorTest.php |
Tests exception isolation and invalid result detection in the executor. |
tests/Reporters/ReporterTest.php |
Validates JSON/SARIF/GitLab/JUnit/console output contracts and redaction. |
tests/Fixtures/competitor-capabilities.php |
Adds a capability/disposition fixture for competitive feature mapping. |
tests/Commands/WardenSyntaxCommandTest.php |
Updates syntax command tests to the new typed audit/result model. |
tests/Commands/WardenInitCommandTest.php |
Tests non-destructive init behavior and pinned CI templates. |
tests/Commands/WardenBaselineCommandTest.php |
Tests baseline generation schema and fingerprint capture. |
tests/Commands/WardenAuditCommandTest.php |
Tests deterministic JSON output, fail-on gating, and structured errors. |
src/ValueObjects/Finding.php |
Adds the typed Finding model with stable fingerprinting and JSON shape. |
src/ValueObjects/AuditResult.php |
Adds typed per-audit result container with status/duration. |
src/ValueObjects/AuditReport.php |
Adds typed full-run report with sorting, summaries, and exit-code logic. |
src/ValueObjects/AuditError.php |
Adds typed audit/config error representation. |
src/ValueObjects/AuditContext.php |
Adds typed context (profile/scope/timeout/selection) with validation. |
src/stubs/gitlab-ci.yml |
Adds a dedicated GitLab CI stub for safe onboarding. |
src/stubs/github-workflow.yml |
Adds a pinned GitHub Actions workflow stub for safe onboarding. |
src/Services/SuppressionService.php |
Implements reviewed suppressions + baseline filtering with strict validation. |
src/Services/Source/TextSourceAnalyzer.php |
Adds text-based secret + Blade checks with redaction-safe identities. |
src/Services/Source/SourceFileDiscovery.php |
Adds Finder-based discovery with size/unreadable guards and path validation. |
src/Services/RulePolicy.php |
Adds rule IDs + default dispositions and override validation/application. |
src/Services/ReportFormatterFactory.php |
Centralizes report formatter selection with a strict format map. |
src/Services/NotificationDispatcher.php |
Consolidates opt-in notifications and makes delivery non-gating. |
src/Services/CustomAuditWrapper.php |
Updates custom audit integration to the typed contract. |
src/Services/Audits/StorageAuditService.php |
Refactors storage/deployment checks into typed findings and profiles. |
src/Services/Audits/SourceAuditService.php |
Adds parser-backed source audit orchestration with timeout handling. |
src/Services/Audits/PlatformAuditService.php |
Adds offline PHP/Laravel support calendar audit with blocking/advisory output. |
src/Services/Audits/PhpSyntaxAuditService.php |
Refactors syntax scanning into typed findings via php -l. |
src/Services/Audits/NpmAuditService.php |
Refactors npm audit to lockfile-only mode with typed findings/errors. |
src/Services/Audits/LaravelConfigAuditService.php |
Adds effective-config production checks (env tracked, TLS URL, cookies, tooling, CORS). |
src/Services/Audits/EnvAuditService.php |
Removes legacy env audit (replaced by new config/source approaches). |
src/Services/Audits/DebugModeAuditService.php |
Removes legacy debug-mode audit (replaced by new config checks/tooling rules). |
src/Services/Audits/ConfigAuditService.php |
Removes legacy config audit (replaced by new typed audits). |
src/Services/Audits/ComposerAuditService.php |
Refactors composer audit to locked/no-dev modes with typed findings/errors. |
src/Services/Audits/AbstractAuditService.php |
Removes the old mutable findings base class. |
src/Services/AuditRunner.php |
Introduces the audit selection/validation/orchestration pipeline. |
src/Services/AuditExecutor.php |
Executes audits safely (exception isolation, result validation, duration). |
src/Services/AuditCacheService.php |
Removes legacy audit caching (2.0 is deterministic and CI-first). |
src/Reporters/SarifReporter.php |
Adds SARIF 2.1.0 report generation. |
src/Reporters/JunitReporter.php |
Adds JUnit XML output for CI/test gate integration. |
src/Reporters/JsonReporter.php |
Adds versioned JSON output for machine consumption. |
src/Reporters/GitLabReporter.php |
Adds GitLab dependency scanning output. |
src/Reporters/GitHubReporter.php |
Adds GitHub Actions annotations output. |
src/Providers/WardenServiceProvider.php |
Updates service provider wiring and registers new commands. |
src/Notifications/Concerns/HasSeverityHelpers.php |
Improves typing/docs around notification severity helpers. |
src/Notifications/Channels/TeamsChannel.php |
Makes Teams delivery throw on HTTP failures and adds typing docs. |
src/Notifications/Channels/SlackChannel.php |
Makes Slack delivery throw on HTTP failures; improves reference handling. |
src/Notifications/Channels/EmailChannel.php |
Adds typing docs and keeps email notification behavior aligned. |
src/Notifications/Channels/DiscordChannel.php |
Makes Discord delivery throw on HTTP failures; improves reference handling. |
src/Examples/DatabasePasswordAudit.php |
Updates example custom audit to the new typed audit contract. |
src/Enums/Severity.php |
Adds a typed severity enum with weights + scanner parsing helper. |
src/Enums/RuleDisposition.php |
Adds rule disposition enum (enforced/advisory/off). |
src/database/migrations/2024_01_01_000000_create_warden_audit_history_table.php |
Removes legacy audit-history migration (feature removed). |
src/Contracts/ReportFormatter.php |
Adds report formatter contract for reporters. |
src/Contracts/NotificationChannel.php |
Adds typing docs for notification payloads. |
src/Contracts/CustomAudit.php |
Refactors custom audit contract to run(AuditContext): AuditResult. |
src/Contracts/AuditServiceInterface.php |
Refactors audit contract to typed results. |
src/config/warden.php |
Updates configuration for 2.0: audits, suppressions, baselines, overrides, notifications. |
src/Commands/WardenSyntaxCommand.php |
Refactors syntax command to use typed audit results and exit codes. |
src/Commands/WardenScheduleCommand.php |
Removes scheduler management command (feature removed). |
src/Commands/WardenInitCommand.php |
Adds safe onboarding command for config + CI stub generation. |
src/Commands/WardenBaselineCommand.php |
Adds baseline generation with expiring reviewed entries. |
resources/schemas/warden-report-2.0.0.json |
Adds versioned JSON schema for the 2.0 report contract. |
phpstan.neon |
Tightens static analysis level and aligns exclusions with 2.0 structure. |
docs/rules.md |
Documents rule IDs, defaults, and purpose for governance/overrides. |
contributing.md |
Updates contributor guidance (matrix, toolchain checks, contracts). |
composer.json |
Updates dependencies, dev tooling, scripts, and supported framework matrix. |
AGENTS.md |
Updates agent guidance to match 2.0 commands and support matrix. |
.github/workflows/tests.yml |
Expands CI matrix across PHP/Laravel versions + adds validate/phpstan/rector/lowest deps. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 9
🧹 Nitpick comments (14)
src/ValueObjects/AuditReport.php (1)
90-122: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winCache
findings()anderrors()results injsonSerialize().Both methods re-collect (and
findings()re-sorts) on every call.jsonSerialize()callsfindings()4 times anderrors()3 times. For reports with many findings, this is wasteful. Storing the results in local variables eliminates the redundant work and improves readability.♻️ Proposed refactor
public function jsonSerialize(): array { + $findings = $this->findings(); + $errors = $this->errors(); + $counts = ['critical' => 0, 'high' => 0, 'medium' => 0, 'low' => 0]; $blocking = 0; - foreach ($this->findings() as $finding) { + foreach ($findings as $finding) { $counts[$finding->severity->value]++; $blocking += $finding->blocking ? 1 : 0; } return [ 'schema_version' => '2.0.0', 'warden_version' => $this->wardenVersion(), 'run' => [ - 'status' => $this->errors() === [] ? 'completed' : 'failed', + 'status' => $errors === [] ? 'completed' : 'failed', 'profile' => $this->context->profile, 'scope' => $this->context->scope, 'scanned_at' => ($this->scannedAt ?? CarbonImmutable::now())->toISOString(), ], 'summary' => [ - 'total' => count($this->findings()), + 'total' => count($findings), 'blocking' => $blocking, - 'advisory' => count($this->findings()) - $blocking, + 'advisory' => count($findings) - $blocking, 'ignored' => count($this->ignoredFindings), - 'errors' => count($this->errors()), + 'errors' => count($errors), 'severity' => $counts, ], 'audits' => $this->audits, - 'findings' => $this->findings(), + 'findings' => $findings, 'ignored_findings' => $this->ignoredFindings, - 'errors' => $this->errors(), + 'errors' => $errors, ]; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/ValueObjects/AuditReport.php` around lines 90 - 122, Cache the results of findings() and errors() in local variables at the start of jsonSerialize(), then reuse those variables for counting, status determination, summary fields, and serialized output. Update the foreach, total/advisory counts, run status, error count, findings, and errors entries to reference the cached results instead of calling the methods repeatedly.src/Services/Source/PhpSourceAnalyzer.php (1)
26-50: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winCreate the PHP parser once and reuse it across files.
A new
ParserFactoryand parser instance is created for every file on line 29.PhpParserparsers are stateless and safe to reuse. For large codebases this adds unnecessary overhead per file.♻️ Proposed refactor
final class PhpSourceAnalyzer { + private readonly \PhpParser\Parser $parser; + public function __construct(private readonly RulePolicy $rulePolicy) { + $this->parser = (new ParserFactory())->createForNewestSupportedVersion(); } /** `@return` array{findings: list<Finding>, errors: list<AuditError>} */ public function analyze(SplFileInfo $file): array { $path = ltrim(str_replace(base_path(), '', $file->getRealPath()), DIRECTORY_SEPARATOR); - $parser = (new ParserFactory())->createForNewestSupportedVersion(); try { - $nodes = $parser->parse($file->getContents()); + $nodes = $this->parser->parse($file->getContents()); } catch (Error $error) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Services/Source/PhpSourceAnalyzer.php` around lines 26 - 50, Initialize and retain a single PhpParser parser instance on the analyzer instead of creating it inside analyze(). Add a parser property and initialize it once in the constructor (or equivalent setup), then update analyze() to reuse that property while preserving the existing parse error handling.src/Services/Audits/LaravelConfigAuditService.php (1)
148-167: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueInconsistent tool detection:
class_existsvsInstalledVersions::isInstalled.Telescope is detected via
class_exists(), while Debugbar and Clockwork useInstalledVersions::isInstalled(). The$toolsarray already contains class names that could be used withclass_existsfor consistency. Consider unifying the detection approach.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Services/Audits/LaravelConfigAuditService.php` around lines 148 - 167, Unify debug-tool detection in the audit logic by using class availability consistently: update the Telescope check and the loop over $tools to use class_exists() with each tool’s class name, rather than mixing class_exists() and InstalledVersions::isInstalled(). Adjust the foreach destructuring and related configuration checks while preserving the existing findings.tests/Commands/WardenSyntaxCommandTest.php (1)
27-37: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a test for the audit-error exit path (exit code 2).
WardenSyntaxCommand::handle()returns2when$auditResult->errorsis non-empty, but no test covers this path. The PR objectives explicitly define exit code2for scanner failures, making this a core contract that should be tested.♻️ Suggested test for the error path
public function testSyntaxFindingExitsOne(): void { $finding = new Finding('quality.php.syntax', 'php-syntax', 'Syntax error', Severity::High, 'Parse error', path: 'app/Broken.php'); $this->mock(PhpSyntaxAuditService::class, function (MockInterface $mock) use ($finding): void { $mock->shouldReceive('run')->once()->andReturn(AuditResult::complete('php-syntax', [$finding])); }); $this->artisan('warden:syntax') ->expectsOutputToContain('app/Broken.php: Parse error') ->assertExitCode(1); } + + public function testAuditErrorExitsTwo(): void + { + $this->mock(PhpSyntaxAuditService::class, function (MockInterface $mock): void { + $mock->shouldReceive('run')->once()->andReturn(AuditResult::failed('php-syntax', 'scanner_error', 'PHP binary not found')); + }); + + $this->artisan('warden:syntax') + ->assertExitCode(2); + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/Commands/WardenSyntaxCommandTest.php` around lines 27 - 37, Add a test alongside testSyntaxFindingExitsOne that mocks PhpSyntaxAuditService::run to return an AuditResult with a non-empty errors collection, invokes the warden:syntax artisan command, and asserts it exits with code 2. Use the existing test conventions and relevant AuditResult error structure to cover WardenSyntaxCommand::handle()’s audit-error path.src/Commands/WardenBaselineCommand.php (2)
76-85: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse the imported
CarbonImmutableinstead of the fully qualified name in theinstanceofcheck.Line 7 already imports
Carbon\CarbonImmutable, so line 82 can useCarbonImmutabledirectly instead of\Carbon\CarbonImmutable.♻️ Proposed refactor
- if (!$expiry instanceof \Carbon\CarbonImmutable || $expiry->format('Y-m-d') !== $expires || $expiry->endOfDay()->isPast()) { + if (!$expiry instanceof CarbonImmutable || $expiry->format('Y-m-d') !== $expires || $expiry->endOfDay()->isPast()) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Commands/WardenBaselineCommand.php` around lines 76 - 85, In the expiry validation within the command’s date parsing logic, replace the fully qualified \Carbon\CarbonImmutable reference in the instanceof check with the already imported CarbonImmutable class name.
113-118: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider extracting the duplicated
csvOption()helper.The
csvOption()method (lines 146–161) is identical to the one inWardenAuditCommand. Extracting it into a shared trait or base command would reduce duplication as both commands evolve.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Commands/WardenBaselineCommand.php` around lines 113 - 118, Extract the duplicated csvOption() method from WardenBaselineCommand and WardenAuditCommand into a shared trait or base command, then update both commands to use the shared implementation while preserving the existing behavior and signature.src/Commands/WardenSyntaxCommand.php (1)
29-31: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDisplay audit errors before returning exit code 2.
The command returns
2when$auditResult->errorsis non-empty but never prints those errors, leaving the user with no visible feedback. WhilePhpSyntaxAuditServicecurrently never produces errors, adding error output future-proofs the command against silent failures.♻️ Proposed fix
if ($auditResult->errors !== []) { + foreach ($auditResult->errors as $error) { + $this->error(sprintf('%s: %s', $error->code, $error->message)); + } return 2; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Commands/WardenSyntaxCommand.php` around lines 29 - 31, Before returning exit code 2 in WardenSyntaxCommand, iterate over $auditResult->errors and output each audit error to the command’s error/output stream, then preserve the existing return code. Use the surrounding command output conventions and the PhpSyntaxAuditService result structure to format the messages clearly.src/Services/Audits/PhpSyntaxAuditService.php (2)
81-91: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winNormalise path separators in
isExcludedfor cross-platform compatibility.Config exclude values use forward slashes (e.g.,
bootstrap/cache), but on Windows the relative path uses backslashes. The checkstr_starts_with($path, rtrim($directory, '/') . DIRECTORY_SEPARATOR)becomesstr_starts_with('bootstrap\cache\file.php', 'bootstrap/cache\')on Windows, which is always false. Files inbootstrap/cachewould not be excluded on Windows.♻️ Proposed fix
/** `@param` list<string> $excluded */ private function isExcluded(string $path, array $excluded): bool { + $normalisedPath = str_replace('\\', '/', $path); foreach ($excluded as $directory) { - if ($path === $directory || str_starts_with($path, rtrim($directory, '/') . DIRECTORY_SEPARATOR)) { + $normalisedDirectory = rtrim(str_replace('\\', '/', $directory), '/'); + if ($normalisedPath === $normalisedDirectory || str_starts_with($normalisedPath, $normalisedDirectory . '/')) { return true; } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Services/Audits/PhpSyntaxAuditService.php` around lines 81 - 91, Normalize both the input path and each excluded directory to a consistent separator before comparing them in isExcluded, converting backslashes to forward slashes and trimming trailing separators. Preserve exact-directory and descendant matching so values such as bootstrap/cache work on Windows and Unix.
28-29: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winCatch
ProcessTimedOutExceptionto prevent uncaught audit crashes.
ComposerAuditServicecatchesProcessTimedOutExceptionand returnsAuditResult::failed, butPhpSyntaxAuditServicedoes not. If aphp -lprocess times out, the exception propagates uncaught and crashes the entire audit run. Whilephp -lis fast, the timeout is configurable down to 1 second, and consistency withComposerAuditServiceis important for reliability.🔒️ Proposed fix
use Symfony\Component\Process\Exception\ProcessTimedOutException; use Symfony\Component\Process\Process; class PhpSyntaxAuditService implements AuditServiceInterface { public function getName(): string { return 'php-syntax'; } public function run(AuditContext $auditContext): AuditResult { $findings = []; foreach ($this->phpFiles() as $path) { $process = new Process([PHP_BINARY, '-l', $path], base_path(), null, null, $auditContext->timeout); - $process->run(); + try { + $process->run(); + } catch (ProcessTimedOutException) { + return AuditResult::failed($this->getName(), 'timeout', sprintf('PHP syntax lint exceeded the %d second timeout on %s.', $auditContext->timeout, $path)); + } if ($process->isSuccessful()) { continue; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Services/Audits/PhpSyntaxAuditService.php` around lines 28 - 29, Update PhpSyntaxAuditService to catch ProcessTimedOutException around the Process execution, matching ComposerAuditService behavior, and return AuditResult::failed with the appropriate timeout error details instead of allowing the exception to escape. Use the existing audit context and result-handling conventions in the service.src/Services/NotificationDispatcher.php (2)
27-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd
Findingtype hint to the closure parameter.The
$findingparameter on line 28 is untyped. Adding theFindingtype hint improves type safety and ensures PHPStan can validate property access.♻️ Proposed fix
- $notificationChannel->send(array_map( - static fn ($finding): array => [ + $notificationChannel->send(array_map( + static fn (Finding $finding): array => [🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Services/NotificationDispatcher.php` around lines 27 - 41, Add the Finding type hint to the $finding parameter of the static closure inside NotificationDispatcher’s notification payload mapping, preserving the existing array mapping logic so PHPStan can validate the accessed properties.Source: Coding guidelines
50-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winResolve notification channels through the container
NotificationDispatcheris hardcoded toSlackChannel,DiscordChannel,TeamsChannel, andEmailChannel, which blocks custom channel registration. Resolve the channel list from the container instead of instantiating the concrete classes directly.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Services/NotificationDispatcher.php` around lines 50 - 54, The channels() method hardcodes notification channel implementations, preventing custom registrations. Inject or access the service container in NotificationDispatcher and resolve all registered NotificationChannel services there, returning the resolved list while preserving the declared list<NotificationChannel> return type.src/Services/AuditRunner.php (2)
22-22: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMake
AuditRunnerfinalfor consistency.Other services in this PR (
NotificationDispatcher,StorageAuditService,CustomAuditWrapper) arefinal. Consider the same here unless subclassing is intended.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Services/AuditRunner.php` at line 22, Declare the AuditRunner class as final, matching NotificationDispatcher, StorageAuditService, and CustomAuditWrapper unless intentional subclassing requires otherwise.
81-89: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winWrap built-in service construction in try-catch.
Custom audit construction (lines 104–122) is wrapped in try-catch with proper error reporting, but built-in service construction is not. If a built-in service throws during
container->make(), the exception propagates unhandled throughrun()and up to the command, producing a non-deterministic exit code instead of a structuredAuditError.🛡️ Proposed fix
$services = [ - $this->container->make(SupplyChainAuditService::class), - $this->container->make(ComposerAuditService::class), - $this->container->make(NpmAuditService::class), - $this->container->make(LaravelConfigAuditService::class), - $this->container->make(PlatformAuditService::class), - $this->container->make(SourceAuditService::class), - $this->container->make(StorageAuditService::class), ]; + + $builtInClasses = [ + SupplyChainAuditService::class, + ComposerAuditService::class, + NpmAuditService::class, + LaravelConfigAuditService::class, + PlatformAuditService::class, + SourceAuditService::class, + StorageAuditService::class, + ]; + + foreach ($builtInClasses as $class) { + try { + $services[] = $this->container->make($class); + } catch (Throwable $throwable) { + $errors[] = new AuditError('configuration', 'builtin_audit_initialization_failed', sprintf('%s: %s', $class, $throwable->getMessage())); + } + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Services/AuditRunner.php` around lines 81 - 89, Wrap the built-in service construction in AuditRunner::run() with the same try-catch and error-reporting behavior used for custom audit construction. Catch exceptions from each container->make() call (including SupplyChainAuditService, ComposerAuditService, and the other built-in services), report them through the existing AuditError mechanism, and ensure run() returns the structured failure result rather than allowing the exception to propagate.tests/Services/NotificationDispatcherTest.php (1)
18-32: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd tests for channel failure and multi-channel dispatch.
The test only covers the happy path with a single configured channel. Consider adding cases for: a channel throwing (verifying warnings are returned), multiple configured channels (verifying
assertSentCountincreases), and empty findings.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/Services/NotificationDispatcherTest.php` around lines 18 - 32, Extend the NotificationDispatcher tests around testConfiguredChannelIsDispatchedExactlyOnce with cases for a channel failure that asserts returned warnings, multiple configured channels that verifies the expected Http::assertSentCount, and an audit report with empty findings that validates the dispatcher’s behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/tests.yml:
- Line 26: Configure both actions/checkout@v4 steps with persist-credentials:
false by adding the option under each checkout action in the workflow.
- Around line 10-55: Add a top-level permissions block to the test workflow with
contents: read, restricting GITHUB_TOKEN access to the read-only permission
required by the checkout and validation steps.
In `@readme.md`:
- Around line 270-276: Remove the duplicate second “## License” section in the
README, keeping only the first License heading and its MIT license description.
In `@src/Commands/WardenInitCommand.php`:
- Around line 141-146: Update WardenInitCommand::stub() to detect
file_get_contents failures and throw an exception (or return an explicitly
handled failure) instead of returning an empty string; ensure its callers,
including writeOwnedFile(), propagate or report the error and do not create or
claim creation of an empty workflow file.
In `@src/Reporters/SarifReporter.php`:
- Around line 66-73: In the SARIF location construction within the reporter
method handling `$finding`, do not create a `region` when `$finding->line` is
null; include `region` with `startLine` only when a line number is available,
while always retaining `artifactLocation`.
In `@src/Services/Audits/LaravelConfigAuditService.php`:
- Around line 145-183: The debug-tool findings in toolingFindings() all share
the same ID, causing fingerprint-less suppressions to affect Telescope,
Debugbar, and Clockwork together. Assign distinct stable IDs per tool, including
the Telescope finding and each entry generated from $tools, while preserving
their existing identities and metadata.
In `@src/Services/Audits/PlatformAuditService.php`:
- Around line 114-130: Change composerPlatformPhp() from private to protected so
PlatformAuditServiceTest can override it alongside phpVersion() and
laravelVersion(), ensuring tests use controlled fixture values instead of
reading the real composer.json.
In `@src/Services/Audits/SourceAuditService.php`:
- Around line 47-58: Add a timeout check immediately after the blade analysis in
the audit method, before iterating over PHP files; when timed out, append the
same `AuditError` used in the PHP loop and skip further analysis, while
preserving existing finding and error aggregation behavior.
In `@tests/Services/Audits/LaravelConfigAuditServiceTest.php`:
- Around line 13-21: The CI and secure-production tests rely on the fixture’s
.env not being tracked, making findings environment-dependent. In
LaravelConfigAuditServiceTest, isolate each test with an explicit temporary base
path or stub trackedEnvironmentFindings() before invoking
LaravelConfigAuditService::run, ensuring both profile tests remain independent
of the Testbench fixture’s base path and git state.
---
Nitpick comments:
In `@src/Commands/WardenBaselineCommand.php`:
- Around line 76-85: In the expiry validation within the command’s date parsing
logic, replace the fully qualified \Carbon\CarbonImmutable reference in the
instanceof check with the already imported CarbonImmutable class name.
- Around line 113-118: Extract the duplicated csvOption() method from
WardenBaselineCommand and WardenAuditCommand into a shared trait or base
command, then update both commands to use the shared implementation while
preserving the existing behavior and signature.
In `@src/Commands/WardenSyntaxCommand.php`:
- Around line 29-31: Before returning exit code 2 in WardenSyntaxCommand,
iterate over $auditResult->errors and output each audit error to the command’s
error/output stream, then preserve the existing return code. Use the surrounding
command output conventions and the PhpSyntaxAuditService result structure to
format the messages clearly.
In `@src/Services/AuditRunner.php`:
- Line 22: Declare the AuditRunner class as final, matching
NotificationDispatcher, StorageAuditService, and CustomAuditWrapper unless
intentional subclassing requires otherwise.
- Around line 81-89: Wrap the built-in service construction in
AuditRunner::run() with the same try-catch and error-reporting behavior used for
custom audit construction. Catch exceptions from each container->make() call
(including SupplyChainAuditService, ComposerAuditService, and the other built-in
services), report them through the existing AuditError mechanism, and ensure
run() returns the structured failure result rather than allowing the exception
to propagate.
In `@src/Services/Audits/LaravelConfigAuditService.php`:
- Around line 148-167: Unify debug-tool detection in the audit logic by using
class availability consistently: update the Telescope check and the loop over
$tools to use class_exists() with each tool’s class name, rather than mixing
class_exists() and InstalledVersions::isInstalled(). Adjust the foreach
destructuring and related configuration checks while preserving the existing
findings.
In `@src/Services/Audits/PhpSyntaxAuditService.php`:
- Around line 81-91: Normalize both the input path and each excluded directory
to a consistent separator before comparing them in isExcluded, converting
backslashes to forward slashes and trimming trailing separators. Preserve
exact-directory and descendant matching so values such as bootstrap/cache work
on Windows and Unix.
- Around line 28-29: Update PhpSyntaxAuditService to catch
ProcessTimedOutException around the Process execution, matching
ComposerAuditService behavior, and return AuditResult::failed with the
appropriate timeout error details instead of allowing the exception to escape.
Use the existing audit context and result-handling conventions in the service.
In `@src/Services/NotificationDispatcher.php`:
- Around line 27-41: Add the Finding type hint to the $finding parameter of the
static closure inside NotificationDispatcher’s notification payload mapping,
preserving the existing array mapping logic so PHPStan can validate the accessed
properties.
- Around line 50-54: The channels() method hardcodes notification channel
implementations, preventing custom registrations. Inject or access the service
container in NotificationDispatcher and resolve all registered
NotificationChannel services there, returning the resolved list while preserving
the declared list<NotificationChannel> return type.
In `@src/Services/Source/PhpSourceAnalyzer.php`:
- Around line 26-50: Initialize and retain a single PhpParser parser instance on
the analyzer instead of creating it inside analyze(). Add a parser property and
initialize it once in the constructor (or equivalent setup), then update
analyze() to reuse that property while preserving the existing parse error
handling.
In `@src/ValueObjects/AuditReport.php`:
- Around line 90-122: Cache the results of findings() and errors() in local
variables at the start of jsonSerialize(), then reuse those variables for
counting, status determination, summary fields, and serialized output. Update
the foreach, total/advisory counts, run status, error count, findings, and
errors entries to reference the cached results instead of calling the methods
repeatedly.
In `@tests/Commands/WardenSyntaxCommandTest.php`:
- Around line 27-37: Add a test alongside testSyntaxFindingExitsOne that mocks
PhpSyntaxAuditService::run to return an AuditResult with a non-empty errors
collection, invokes the warden:syntax artisan command, and asserts it exits with
code 2. Use the existing test conventions and relevant AuditResult error
structure to cover WardenSyntaxCommand::handle()’s audit-error path.
In `@tests/Services/NotificationDispatcherTest.php`:
- Around line 18-32: Extend the NotificationDispatcher tests around
testConfiguredChannelIsDispatchedExactlyOnce with cases for a channel failure
that asserts returned warnings, multiple configured channels that verifies the
expected Http::assertSentCount, and an audit report with empty findings that
validates the dispatcher’s behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 16abf9de-fdbc-4d86-b8b2-234e522e6512
⛔ Files ignored due to path filters (1)
composer.lockis excluded by!**/*.lock
📒 Files selected for processing (83)
.github/workflows/tests.ymlAGENTS.mdUPGRADE.mdcomposer.jsoncontributing.mddocs/rules.mdphpstan.neonreadme.mdresources/schemas/warden-report-2.0.0.jsonsrc/Commands/WardenAuditCommand.phpsrc/Commands/WardenBaselineCommand.phpsrc/Commands/WardenInitCommand.phpsrc/Commands/WardenScheduleCommand.phpsrc/Commands/WardenSyntaxCommand.phpsrc/Contracts/AuditServiceInterface.phpsrc/Contracts/CustomAudit.phpsrc/Contracts/NotificationChannel.phpsrc/Contracts/ReportFormatter.phpsrc/Enums/RuleDisposition.phpsrc/Enums/Severity.phpsrc/Examples/DatabasePasswordAudit.phpsrc/Notifications/Channels/DiscordChannel.phpsrc/Notifications/Channels/EmailChannel.phpsrc/Notifications/Channels/SlackChannel.phpsrc/Notifications/Channels/TeamsChannel.phpsrc/Notifications/Concerns/HasSeverityHelpers.phpsrc/Providers/WardenServiceProvider.phpsrc/Reporters/ConsoleReporter.phpsrc/Reporters/GitHubReporter.phpsrc/Reporters/GitLabReporter.phpsrc/Reporters/JsonReporter.phpsrc/Reporters/JunitReporter.phpsrc/Reporters/SarifReporter.phpsrc/Services/AuditCacheService.phpsrc/Services/AuditExecutor.phpsrc/Services/AuditRunner.phpsrc/Services/Audits/AbstractAuditService.phpsrc/Services/Audits/ComposerAuditService.phpsrc/Services/Audits/ConfigAuditService.phpsrc/Services/Audits/DebugModeAuditService.phpsrc/Services/Audits/EnvAuditService.phpsrc/Services/Audits/LaravelConfigAuditService.phpsrc/Services/Audits/NpmAuditService.phpsrc/Services/Audits/PhpSyntaxAuditService.phpsrc/Services/Audits/PlatformAuditService.phpsrc/Services/Audits/SourceAuditService.phpsrc/Services/Audits/StorageAuditService.phpsrc/Services/Audits/SupplyChainAuditService.phpsrc/Services/CustomAuditWrapper.phpsrc/Services/NotificationDispatcher.phpsrc/Services/ReportFormatterFactory.phpsrc/Services/RulePolicy.phpsrc/Services/Source/PhpSourceAnalyzer.phpsrc/Services/Source/SourceFileDiscovery.phpsrc/Services/Source/TextSourceAnalyzer.phpsrc/Services/SuppressionService.phpsrc/ValueObjects/AuditContext.phpsrc/ValueObjects/AuditError.phpsrc/ValueObjects/AuditReport.phpsrc/ValueObjects/AuditResult.phpsrc/ValueObjects/Finding.phpsrc/config/warden.phpsrc/database/migrations/2024_01_01_000000_create_warden_audit_history_table.phpsrc/stubs/github-workflow.ymlsrc/stubs/gitlab-ci.ymltests/Commands/WardenAuditCommandTest.phptests/Commands/WardenBaselineCommandTest.phptests/Commands/WardenInitCommandTest.phptests/Commands/WardenSyntaxCommandTest.phptests/Fixtures/competitor-capabilities.phptests/Reporters/ReporterTest.phptests/Services/AuditExecutorTest.phptests/Services/Audits/DebugModeAuditServiceTest.phptests/Services/Audits/DependencyAuditServiceTest.phptests/Services/Audits/LaravelConfigAuditServiceTest.phptests/Services/Audits/PlatformAuditServiceTest.phptests/Services/Audits/SourceAuditServiceTest.phptests/Services/Audits/SupplyChainAuditServiceTest.phptests/Services/CompetitorCapabilityTest.phptests/Services/NotificationDispatcherTest.phptests/Services/SuppressionServiceTest.phptests/TestCase.phptests/ValueObjects/AuditReportTest.php
💤 Files with no reviewable changes (8)
- src/database/migrations/2024_01_01_000000_create_warden_audit_history_table.php
- src/Services/Audits/AbstractAuditService.php
- tests/Services/Audits/DebugModeAuditServiceTest.php
- src/Commands/WardenScheduleCommand.php
- src/Services/Audits/DebugModeAuditService.php
- src/Services/Audits/ConfigAuditService.php
- src/Services/Audits/EnvAuditService.php
- src/Services/AuditCacheService.php
Adds safer audit execution and reporting: separate Telescope, Debugbar, and Clockwork into distinct rule IDs, surface PHP syntax timeouts and command errors, reuse parser/process setup more efficiently, and make SARIF/report output handle missing line/version data cleanly. Also updates init and workflow handling plus related docs/tests.
Summary
Warden 2.0 refocuses the package as a deterministic, CI-first security gate for Laravel applications.
This release introduces typed audit contracts, dependable exit codes, parser-backed source analysis, supply-chain protections, governed suppressions and baselines, machine-readable reports, safe CI initialization, and a substantially clearer console experience.
What changed
Dependable CI security gates
Finding,AuditResult,AuditError,AuditReport, andAuditContextobjects.0— scan completed without findings meeting the failure threshold.1— blocking findings met--fail-on.2— incomplete scan, invalid configuration, scanner failure, timeout, or malformed output.--severitywith--fail-on.ci,production, andlocalprofiles.Application source scanning
Added a default parser-backed
sourceaudit using PHP-Parser and Symfony Finder.Coverage includes:
PHP files are parsed once and analysed using lightweight intraprocedural taint tracking. Credentials are always redacted and contribute only a one-way hash to fingerprints.
Unreadable, oversized, or unparseable selected files mark the scan incomplete and return exit code
2.Composer, npm, and supply-chain auditing
package-lock.jsonis present.secure-http=false.--scope=all.Laravel and platform checks
env()..envis valid in CI..envis tracked.Rule policy, suppressions, and baselines
rule_overrideswith:enforcedadvisoryoffidreasonexpires_at2.warden:baselinefor explicitly accepting existing fingerprints while continuing to detect new findings.Reports and console output
Added standardized formats:
The console report now provides:
A versioned JSON schema is included at
resources/schemas/warden-report-2.0.0.json.Safe onboarding
Added:
The command:
--forceto replace only Warden-owned generated files.Notifications
--notify.Removed features
Removed features that conflicted with Warden being a development-only CI dependency:
Breaking changes
--severitybecomes--fail-on.--outputbecomes--format.--npmis removed; npm is auto-detected.--force,--no-notify, andwarden:scheduleare removed.2.See
UPGRADE.mdfor the full migration guide.CI coverage
The GitHub Actions matrix now covers:
Validation
composer validate --strictpasses.git diff --checkpasses.Summary by CodeRabbit
warden:auditwith profile/scope selection,--fail-on,--only/--skip, and machine-readable output to stdout or--output-file.warden:baselinefor expiring JSON baselines and suppression handling.warden:initto publish configuration and generate GitHub/GitLab CI integration files.--notify.