Smart 404 automatically logs every 404 response in a dedicated, aggregated database table (one row per normalized path repeated hits increment a counter) and provides an administration UI to review the log and fix broken URLs quickly.
From the overview page, site managers can create redirects (via the Redirect module) with a single click, with destination suggestions based on existing URL aliases (fragment match + Levenshtein ranking), process paths in bulk, or exclude paths from logging with glob patterns.
Privacy is a baseline: no IP addresses are stored, user-agent strings are only categorized (bot/browser/unknown) and never stored raw, and external referers are reduced to their domain.
Comments
Comment #2
avpadernoThank you for applying!
Before giving links helpful to understand how the review process works, what to expect from a review, and what to do to avoid a review takes more time than needed, I would like to thank all the reviewers for the work they do.
These applications are volunters-driven, which also means it is not possible to predict when an application will be marked fixed and the applicant will get the permission to opt projects into security advisory policy. While we aim to make an application as quick as possible, it is also important for us that more people review the project used for an application. In this way, we make sure applications do not miss some important points that should be instead reported.
Applications are not meant to be complete debugging sessions that eliminate every existing bug, though. I apologize if sometimes applications seem to go into too-detailed reviews.
Please read Review process for security advisory coverage: What to expect for more details and Security advisory coverage application checklist to understand what reviewers look for. Tips for ensuring a smooth review gives some hints for a smoother review. See also Policy on the use of AI when contributing to Drupal, which is valid when contributing to Drupal, either by committing code in a project, or by creating a merge request for an existing project.
The important notes are the following.
Keep in mind that once the project is opted into security advisory coverage, only Security Team members may change coverage.
To the reviewers
Please read How to review security advisory coverage applications, Application workflow, What to cover in an application review, and Tools to use for reviews.
The important notes are the following.
For new reviewers, I would also suggest to first read In which way the issue queue for coverage applications is different from other project queues.
Comment #3
vishal.kadamComment #4
vishal.kadam1. FILE: README.md
The README file is missing the required sections - Installation and Configuration.
2. FILE: smart_404.libraries.yml
version: VERSIONVERSION is only used by Drupal core modules. Contributed modules should use a literal string that does not change with the Drupal core version a site is using.
3. FILE: smart_404.module
A new module that aims to be compatible with latest Drupal releases is expected to implement hooks as class methods as described in Support for object oriented hook implementations using autowired services.
4. FILE: src/Form/Smart404IgnoreForm.php and src/Form/Smart404SettingsForm.php
With Drupal 10 and Drupal 11, there is no longer need to use #default_value for each form element, when the parent class is ConfigFormBase: It is sufficient to use #config_target, as in the following code.
Using that code, it is no longer needed to save the configuration values in the form submission handler: The parent class will take care of that.
Comment #5
mookum commentedThank you for the review! All four points are addressed in the latest commit on 1.0.x:
1. README.md now contains the Installation and Configuration sections from the README template, plus a Maintainers section.
2. smart_404.libraries.yml now declares a literal version ('1.0') instead of VERSION.
3. hook_help() and hook_cron() are now implemented as methods on \Drupal\smart_404\Hook\Smart404Hooks using #[Hook] attributes, with #[LegacyHook] procedural wrappers kept for Drupal core < 11.1, since the module supports ^10.3.
4. Both ConfigFormBase forms now use #config_target. The settings form no longer overrides submitForm(); the ignore-patterns form keeps a submitForm() override only to delete already-logged records matching the new patterns, after letting the parent save the configuration
Comment #6
abdelwahiedReviewed 1.0.x (commit e5d08e1) on Drupal 11.
AUTOMATED REVIEW
phpcs (Drupal + DrupalPractice) is clean on all PHP/YAML — no errors. The only violations are 2 auto-fixable coding-standards errors in js/smart_404.admin.js (missing spaces around a ternary "?", line 34). Running phpcbf / prettier on that file fixes them.
MANUAL REVIEW — POSITIVES
- Clean dependency injection throughout src/; no \Drupal:: static calls in business logic.
- Database access goes entirely through the DB API (merge()->key(), select(), update(), delete() with condition()/fields()/execute()) — fully parameterized, no raw SQL.
- No stored XSS on the logged 404 paths: the overview form renders the path via Link::fromTextAndUrl() (auto-escaped title), the referer as a plain-string cell (Twig auto-escapes), and the detail controller escapes with a dedicated escapeHtml() helper inside #markup.
- Good structure (repository, normalizer, glob matcher, suggestion engine), tests present, README + CHANGELOG.
MINOR
- Smart404OverviewForm uses @parse_url($record->referer_last). The @ error-suppression operator is discouraged by Drupal standards; prefer checking the return value (parse_url() returns false on malformed input) without silencing.
Nice, well-organized module. Fix the two JS nits and it looks in good shape.
Comment #7
mookum commentedThanks for the review!
- Fixed the JS spacing nit, turned out values[p75Index] is always in range once values.length > 0, so the ?? values[values.length - 1] fallback was dead code and confusing the ternary-spacing sniff. Removed the fallback rather than just adding spaces.
- Replaced the @parse_url() error suppression in Smart404OverviewForm with an explicit !== FALSE check.
Both fixed in commit 9d2fb93 on 1.0.x. Pipeline should be green — moving back to Needs review.
Comment #8
santerref commentedFollowing up on the @parse_url() fix in Smart404OverviewForm.php (comment #6/#7): the same pattern is still present in src/Logger/Smart404Logger.php, in sanitizeReferer():
Same fix applies here: check for FALSE explicitly instead of suppressing with @.
Everything else I checked (DB API usage, dependency injection, hook implementations) looks solid.
Comment #9
avpadernoThe error control operator is used to suppress diagnostic errors that might be output; when no diagnostic error is output, it does not make sense to use the error control operator.
In this case, the used code and the following code are equivalent.
Comment #10
mookum commentedGood catch, fixed in 81f37bd on 1.0.x. sanitizeReferer() already checks $parsed === FALSE right after the call, so the @ was redundant, removed it.
Comment #11
vishal.kadamI am changing priority as per Issue priorities.
Comment #12
mookum commentedManual review of other projects
- Neon CRM Events
- Paragraphs Usage Manager
- File Bulkupload Translations
Comment #13
avpadernoComment #14
avpadernosrc/Controller/Smart404DetailController.php
With Drupal 10.2.x and higher versions, a controller class does not need to implement
create(); usingAutowireTrait, it just needs to implement the class constructor.Since that class does not use methods from the parent class, or it uses a single method from the parent class, it does not need to use
ControllerBaseas parent class.Controllers do not need to have a parent class; as long as they implement
\Drupal\Core\DependencyInjection\ContainerInjectionInterface, they are fine.src/Hook/Smart404Hooks.php
What the documentation comment says is correct: Drupal 11.2.x invokes both
hook_runtime_requirements()andhook_requirements(). In that case, the following code should return an empty array when$phaseis'runtime'.Still, that code does not work on Drupal 10.3.x, which does not invoke
hook_runtime_requirements(). It does not even work with Drupal 11.1.x, with which the project is supposed to work.src/Form/Smart404BulkRedirectForm.php
@countneeds to be used also for the singular string because there are languages where that is used also when English would use the plural string. For example, in Russian the translation for 1 log file and 101 log files are 1 файл журнала and 101 файл журнала, which literally are 1 log file and 101 log file.Comment #15
avpadernoComment #16
mookum commented@avpaderno
Thanks for catching this.
Fixed all three points on 1.0.x:
1. Smart404DetailController: removed the redundant create()
(and did the same for every other controller/form that had one purely re-implementing what ControllerBase/FormBase already provide via AutowireTrait since Drupal 10.2).
Kept it on the two ConfigFormBase-based forms, since that base class's own constructor takes a TypedConfigManagerInterface I'd rather not blindly re-declare without checking its exact availability across every core version this module supports.
2. Smart404BulkRedirectForm pluralization: fixed and found the same missing @count in the singular string on three other formatPlural() calls elsewhere in the module (Smart404IgnoreConfirmForm, Smart404OverviewForm), fixed those too.
3. Smart404Hooks.php / hook_runtime_requirements():
I looked into this and could confirm hook_runtime_requirements() was introduced in 11.2.0, and that our procedural smart_404_requirements() fallback (with the $phase !== 'runtime' check) already matches the #[LegacyRequirementsHook] contract as documented on the attribute class itself.
I couldn't find what specifically breaks on 11.1.x as opposed to 10.3.x from that alone, could you share the error or the scenario you have in mind?
Happy to fix it once I can reproduce or understand the exact failure.
Comment #17
mookum commentedComment #18
mookum commentedFollowing up on the review bonus process, here are three reviews I've completed for other project applications in the queue:
- [Media/Document Notifier]
- [Random Name Chooser]
- [Entity Save And Add Another]
Requesting a priority bump for Smart 404 (this issue) per the review bonus program. Thanks!
Comment #19
rym_oueslati commentedManual review of 1.0.x at 16282aa. I read the module in full rather than re-running the points already covered in comments 4 to 10, and concentrated on what has landed since 81f37bd on 26 July -- which is a lot: 63 files and 7944 added lines, most of it the test suite and the redirect-validation hardening. I first read the tree at 205f92b and re-checked everything below against 16282aa after the six commits of 28 and 29 August. Pipeline 940809 is green on this commit.
I did not find anything I would call a defect. Rather than say only that, here is what I actually tried to break, since a negative result is only worth something if you can see what was tested.
What I probed, and why it holds
Three small things
For what it is worth as an outside opinion: the density of "why this and not the obvious thing" comments is the most useful part of this codebase. Delegating to Redirect::generateHash() instead of re-implementing its normalization, and asking findMatchingRedirect() for the chained destination instead of analysing the new redirect statically, are both the right call for the same reason, and the comments say so.
Leaving the status at Needs review -- none of the three points above is a reason to move it to Needs work.
Comment #20
avpadernoComment #21
mookum commentedCorrection on the review bonus request: one of the three reviews I originally linked (Entity Add Another, issue 3560166) mistakenly reviewed the wrong codebase.
I misread the issue's project link and picked up an unrelated "similar project" mentioned in the body instead of the actual module under application.
That application has since been approved, so I won't be posting a correction there, but wanted to flag it here rather than let an invalid link stand.
In its place, here's a review I completed for a different application still in the queue:
- [Basic Ads]
Combined with the two originally-linked reviews below, that's three valid reviews:
- [Media/Document Notifier]
- [Random Name Chooser]
- [Basic Ads]
Apologies for the mix-up, and thanks for your patience sorting it out.
Comment #22
nickolajHi,
I reviewed the
1.0.xbranch of Smart 404.This is a manual read of the PHP, routing, permissions, and the admin JS, not a paste of PHPCS/PHPStan.
The project page and README match the code: aggregated 404 log, no raw IP, raw user-agent not stored (only an is_bot flag), external referers stored as domain only.
Smart404Loggerdoes that beforeSmart404Repository::upsert().Permissions in
smart_404.permissions.ymlall haverestrict access: true. That is justified.view smart 404 logcan surface aliases that point at unpublished content (the permission description already says so).create smart 404 redirectscan write Redirect entities without "Administer redirects", butRedirectDestinationValidator::isValidInternalPath()refuses//, backslash,://, control characters, and./..segments, then runs the path throughUrl::fromUserInput(). I also checked the Ignore route:smart_404.ignore_singlehas_csrf_token: TRUEand requires both view and manage permissions.SQL goes through the Database API (
merge()/ conditions). I did not find concatenated statements.GlobMatcherturns admin glob patterns into a regex. That is only reachable withadminister smart 404 settings, which is restricted. I am not asking for a change.Branch
1.0.x,core_version_requirement: ^10.3 || ^11, enough PHP, no third-party library in the tree, Redirect is a proper composer/project dependency. README is detailed. Tests cover access, logging, and the redirect validator, including ajavascript://referer case.I have no blocking findings. Thank you for the careful work on the open-redirect checks in particular.
Comment #23
avpadernoComment #24
avpadernoComment #25
avpadernoThank you for your contribution and for your patience with the review process!
I am going to update your account so you can opt into security advisory coverage any project you create, including the projects you already created.
These are some recommended readings to help you with maintainership:
You can find more contributors chatting on Slack or IRC in #drupal-contribute. So, come hang out and stay involved!
Anyone is welcome to participate in the review process. Please consider reviewing other projects that are pending review. I encourage you to learn more about that process and join the group of reviewers.
I thank also all the reviewers for helping with these applications.
Comment #26
avpaderno