Closed (fixed)
Project:
Drupal.org security advisory coverage applications
Component:
module
Priority:
Normal
Category:
Task
Assigned:
Reporter:
Created:
26 Nov 2025 at 18:19 UTC
Updated:
17 Mar 2026 at 13:15 UTC
Jump to comment: Most recent
Comments
Comment #2
avpadernoComment #3
avpadernoThank you for applying!
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.
The important notes are the following.
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 #4
avpadernosecure_password_reset_log.module
For a new module that aims to be compatible with Drupal 10/11, it is expected it implements hooks as class methods as described in Support for object oriented hook implementations using autowired services.
It requires increasing the minimum required Drupal 10 version, but that is not an issue, since not all the Drupal 10 releases are currently supported.
Drupal core does not use that hook, which is also not necessary, as view plugins are discovered like any other Drupal plugin.
src/Controller/LogController.php
Since that class uses two methods 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.With
AutowireTrait, that code is no longer necessary.src/EventSubscriber/PasswordResetSubscriber.php
With Drupal 11, classes should use property promotion.
Any dependency needs to be obtained via dependency injection.
Comment #5
vishal.kadam1. FILE: README.md
The README file is missing the required section - Requirements.
2. FILE: secure_password_reset_log.module
Drupal does not have primary and secondary hooks. Instead of that, it is preferable to use the usual description: “Hook implementations for the [module name] module”, where [module name] is the name of the module given in its .info.yml file.
3. FILE: src/Controller/LogController.php
FILE: src/EventSubscriber/PasswordResetSubscriber.php
FILE: src/Form/ClearLogsForm.php
FILE: src/Form/DeleteLogForm.php
FILE: src/Plugin/views/field/DeleteLogLink.php
New modules, which are compatible with Drupal 10 and higher versions are expected to include type declarations in property definitions, and use constructor property promotion.
4. FILE: src/Plugin/views/field/DeleteLogLink.php
* @ViewsField("secure_password_reset_log_delete_link")FILE: src/Plugin/views/field/UserStatus.php
* @ViewsField("secure_password_reset_log_user_status")Projects that are compatible with Drupal 10 or higher versions should use attributes instead of annotations.
5. Fix the warnings/errors reported by PHP_CodeSniffer.
Note: I would suggest enabling GitLab CI for the project, follow the Drupal Association .gitlab-ci.yml template and fix the PHP_CodeSniffer errors/warnings it reports.
Comment #6
zeeshan_khan commented@avpaderno - Thank you so much for reviewing my code I have made suggested changes.
@vishal.kadam - Thank you for your detailed review and your time I have made your suggested changes as well.
Comment #7
vishal.kadamI am changing priority as per Issue priorities.
Comment #8
bbu23Hi, here's a partial review from taking a quick look at the code:
- Missing schema for the configuration object provided by the module in
config/install.- New modules, which are compatible with Drupal 10 and higher versions are expected to include type declarations in property definitions, and use property promotion. Update all classes.
- The module is not dependent on the core views module, therefore it should not provide a views configuration file in
config/install, but instead it should be moved toconfig/optional. Unless the views module is required and it becomes a dependency.- The Kernel test doesn't bring anything beneficial, it's redundant. Update the tests to actually test the code.
- Is the use of
$this->killSwitch->trigger();really needed?- With Drupal 10 and Drupal 11, there is no longer need to use
#default_valuefor each form element, when the parent class isConfigFormBase: It is sufficient to use#config_target, as in the following code:- Ensure all string are translatable everywhere. For example, the following code doesn't comply:
Comment #9
bbu23Comment #10
zeeshan_khan commented@bbu23 - Thanks for your review!
I have fixed all the issues you mentioned also reviewed other traces of missing standards.
Comment #11
oivanov commented**Review of secure_password_reset_log (1.0.x branch, commit 512a79a)**
This is a re-review. I checked all the issues raised by avpaderno, vishal.kadam, and bbu23. Some were addressed, but several remain unfixed and I found additional issues. Setting back to **Needs work**.
---
### Blockers
1. **`.DS_Store` committed to repo** -- `src/Plugin/.DS_Store` is a macOS artifact tracked by git. Remove it (`git rm`) and add to `.gitignore`.
2. **Config schema in wrong directory** -- The schema file is at `schema/secure_password_reset_log.schema.yml` but Drupal expects `config/schema/secure_password_reset_log.schema.yml`. It will not be discovered in its current location. This was flagged in the previous review as "missing config schema" -- the file was created but placed incorrectly.
3. **Views config has NO access control** -- In `config/optional/views.view.password_reset_logs.yml`, the access section is:
```yaml
access:
type: none
options: { }
```
This means **anonymous users** can view the password reset logs at `/password-reset-logs`, which contain emails, IPs, user IDs, and reset attempt details. This is a serious security issue. Set `type: perm` with permission `view secure password reset logs`.
4. **`is_non_existing_user` field in views data references non-existent column** -- In `secure_password_reset_log.views.inc` line 80, the views data defines `$data[$table]['is_non_existing_user']` but the database schema column is `user_exists`. This will cause Views queries to fail.
5. **Translated strings stored in database** -- In `PasswordResetSubscriber.php`, `$this->t()` results are passed to `logEvent()` and stored in the DB `details` column. Store English strings in the DB and translate at display time.
6. **DeleteLogForm clears ALL flood records** -- When deleting a single log entry, `DeleteLogForm::submitForm()` runs `$this->database->delete('flood')->condition('event', 'password_reset_attempt')->execute()` which clears flood protection for **all** IPs, not just the one associated with the deleted log. This undermines flood protection.
---
### Previous feedback not fully addressed
7. **Property promotion** -- Previous reviewers requested constructor property promotion. `LogController` uses it, but `ClearLogsForm`, `DeleteLogForm`, and `DeleteLogLink` still use the old pattern:
```php
protected $database;
public function __construct(Connection $database) {
$this->database = $database;
}
```
8. **OOP hooks** -- `hook_views_data()` is still in a procedural `.views.inc` file. Previous feedback asked for OOP hooks with `#[Hook]` attributes.
9. **No tests** -- The module has no tests at all (no unit, kernel, or functional tests). For a security module handling flood control and sensitive log data, tests are expected.
---
### Additional issues found
10. **Dead code in LogController** -- The `delete()` and `clearAll()` methods are unused. The routes `secure_password_reset_log.delete` and `secure_password_reset_log.clear_all` both point to form classes, not these controller methods. These methods also reference a non-existent route `secure_password_reset_log.view`. Remove them.
11. **ClearLogsForm bypasses Flood API** -- Directly queries the `flood` table instead of using `FloodInterface::clear()`. The `flood` table is an implementation detail.
12. **Stale comments in PasswordResetSubscriber** (lines 85-89) -- Comments say "In a real scenario, you should inject the entityTypeManager" and "For a quick fix without changing the constructor" but the entity type manager IS already injected. These are copy-paste leftovers.
13. **Stale @see references** -- Both constants reference `\Drupal\secure_password_reset_log\EventSubscriber\SecurePasswordResetEventSubscriber` which does not exist. The actual class is `PasswordResetSubscriber`.
14. **Inconsistent constant visibility** -- `DEFAULT_MAX_ATTEMPTS` has `public` visibility but `DEFAULT_WINDOW` does not. Both should be explicit.
15. **UserStatus views plugin uses inline styles** -- `#markup` with inline `style` attributes instead of CSS classes and proper render arrays. Also contains unicode symbols in `$this->t()` translation strings.
16. **Bare Response for 429** -- `PasswordResetSubscriber` returns `new Response('...', 429)` bypassing Drupal's theming. Use `TooManyRequestsHttpException` instead.
17. **LogController has no pagination** -- Hard-coded `range(0, 50)` with no pager. Use `PagerSelectExtender`.
18. **README formatting** -- Maintainer link syntax is broken: `[zeeshan_khan]https://www.drupal.org/u/zeeshan_khan` (missing parentheses). "SUPPORT" and "LICENSE" headers are missing `##` markers.
Comment #12
bbu23@oivanov since u are using AI to help drafting your feedback, u could at least make an effort to format the output from Markdown to html or plain text so that people can easily read it...
Comment #13
oivanov commentedme personally - I prefer Markdown to everything else, so I make an effort not to have to deal with HTML (definitely) or plain text (when I can).
If markdown is not acceptable - we can add that to the guidance. Let me know if you want me to do that
Comment #14
zeeshan_khan commented@oivanov - Thanks for your detailed review I have addressed all of the issues mentioned in your comment.
Comment #15
oivanov commentedgreat job! All 6 critical blockers and all 3 major issues have been addressed. This is a solid improvement. I see some minor issues still left -
1. ViewsHooks uses bare t() function — src/Hook/ViewsHooks.php uses the global t() function (lines 24-29, etc.) instead of $this->t() with StringTranslationTrait. Since this is now an OOP hook class, it should use the trait.
2. @see on constants are self-referential — Both DEFAULT_MAX_ATTEMPTS and DEFAULT_WINDOW have @see pointing to the class they're defined in (PasswordResetSubscriber). Harmless but pointless — either remove them or point to where the constants are used.
3. FloodSettingsForm missing declare(strict_types=1) — Every other PHP file has it; this one doesn't. Inconsistency.
4. services.yml has redundant arguments with autowire: true — The event subscriber service has both autowire: true and explicit arguments: list. With autowiring, the arguments are unnecessary.
Those are cosmetic/minor, but we might as well address them since we're here :-)
Comment #16
zeeshan_khan commented@oivanov
Thank you so much for the incredibly thorough review!
I truly appreciate the time and attention you’ve put into this - especially going through even the minor and cosmetic details. The feedback is very clear and constructive, and it definitely helps improve the overall quality and consistency of the module.
I've addressed the remaining minor points (StringTranslationTrait usage in ViewsHooks, the self-referential removed - @see annotations, Added the missing declare(strict_types=1), and removed the redundant service arguments with autowiring) and pushed.
Thanks again for the detailed review and guidance — it’s genuinely helpful and much appreciated!
Comment #17
batigolixHere is some feedback to improve the README.md
None of this is required for the security coverage application, but it does help users that are interested in using your module.
Review is based on drupal.org README template.
##headings use ALL CAPS (## REQUIREMENTS,## INSTALLATION, etc.). The template requires sentence case:## Requirements,## Installation.#heading. Remove sections that don't belong: LICENSE (handled by drupal.org), ROADMAP (use the issue queue), SUPPORT, UNINSTALLATION, and the duplicate DEPENDENCIES (already covered by Requirements).Drupal 11 Security Module,Author: Community Contribution,Package: Securitylook like structured data for automated parsing, not human-readable content. Remove them./modules/custom/. Contributed modules should be installed via Composer. Use the standard text: "Install as you would normally install a contributed Drupal module. Visit https://www.drupal.org/node/1897420 for further information."-dash markers. Use proper Markdown lists throughout.A good reference to follow: Views GeoJSON README.
Comment #18
batigolixComment #19
zeeshan_khan commented@batigolix - Good catch!
Thanks for pointing out small details in Readme file.
I have fixed all of the mentioned issues.
Comment #20
batigolixThe readme is very good now
Comment #21
zeeshan_khan commented@batigolix Thanks
Can you move it to RTBC then?
Comment #22
batigolixI am not so familiar with the exact process for these applications. But I understand from #539608: What to expect from the review process that I am allowed to set it RTBC if there are no more issues found.
Did you address all the reported issues?
I'll give other reviewers a chance to have another look and if there is nothing to report I will set it RTBC next week.
Comment #23
zeeshan_khan commented@batigolix - Thank you for the clarification.
Yes, I’ve addressed all the reported issues to the best of my knowledge. Please let me know if you spot anything else that needs improvement.
I appreciate you giving other reviewers a chance to take another look. Thanks again for your time and support!
Comment #24
oivanov commentedI re-reviewed issues that I'm familiar with. All issues from my previous reviews have been addressed:
Critical issues — fixed:
- .DS_Store removed from repo (added to .gitignore)
- Config schema moved to config/schema/
- Views access now properly requires 'view secure password reset logs' permission
- is_non_existing_user column renamed to user_exists throughout (schema, views data, subscriber)
- Database-stored strings no longer use $this->t()
- Single log deletion now clears flood only for that entry's IP, not all IPs
Major issues — fixed:
- Constructor property promotion used throughout
- OOP hooks via #[Hook] attribute (ViewsHooks)
- Tests added: 2 Functional (access control, flood protection) + 2 Kernel (schema, views data)
Follow-up issues — fixed:
- StringTranslationTrait added to ViewsHooks
- Self-referential @see annotations removed
- declare(strict_types=1) added to all PHP files
- Redundant service arguments removed (autowire)
Code looks clean, tests cover the critical paths. Setting to RTBC.
Comment #25
zeeshan_khan commentedThankyou @oivanov
Comment #26
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 #27
avpadernoComment #29
zeeshan_khan commented@avpaderno Thankyou so much
And thanks to all the reviewers!