Closed (fixed)
Project:
Drupal.org security advisory coverage applications
Component:
module
Priority:
Normal
Category:
Task
Assigned:
Issue tags:
Reporter:
Created:
22 May 2024 at 04:48 UTC
Updated:
11 Sep 2024 at 15:54 UTC
Jump to comment: Most recent
Comments
Comment #2
vishal.kadamThank 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.
phpcs --standard=Drupal,DrupalPracticeon the project, which alone fixes most of what reviewers would report.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. Fix phpcs issues.
2. FILE: donorperfect.module
The usual description for a .module file is Hook implementations for the [module name] module. where [module name] is the module name given in the .info.yml file.
Comment #5
brandonpost commentedHello @vishal.kadam, thanks a lot for the feedback. I think all the corrections have been made.
Comment #6
vishal.kadamRest looks fine to me.
Let’s wait for a Code Review Administrator to take a look and if everything goes fine, you will get the role.
Comment #7
avpadernoThere are changes that need to be done. I will make a review in the next 30 minutes.
Comment #8
avpadernosrc/Controller/ControllerBase.php
The parent class already defines
container()and it is not necessary to defineconfigFactory(), since there is alreadyconfig()in the parent class.src/Controller/DonorPerfectController.php
Dependencies must be added using the
create()method, like done from theAdminControllerclass, theContactControllerclass, or theShortcutControllerclass, which all extend theControllerBaseclass.The
Markupclass is marked@internal. It must not be used by contributed modules, and there should not be any reason to use it, since Drupal core allows to use a render array or a string containing markup.Strings shown in the user interface must be translatable. Drupal core has also a class/method for strings that depend on a dynamic value (a string when the value is singular; a string when the value is plural).
$link = Markup::create('<a href="#" result-data=' . str_replace(' ', ' ', Json::encode($donor_data)) . '>' . $this->t('Match') . '</a>');There is a class to generate links.
src/Entity/Storage.php
SQL queries are not built concatenating strings. Placeholders must be used.
src/Entity/Query/Condition.php
Drupal\Core\Entity\Query\Sql\Conditionis not part of the public API and cannot be extended from contributed modules.Drupal\Core\Entity\Query\ConditionBaseis a base class and can be extended.src/Entity/Query/QueryFactory.php
The
Drupal\Core\Entity\Query\Sql\QueryFactoryis not part of the public API too. In this case, it does not even make sense to extend a class and not change any defined method.src/DPQuery.php
ControllerBase is a class for controllers. It cannot be used for a generic class that is not used for controllers.
src/DPUtility.php
If a class uses
\Drupal's methods, then it should probably be converted to a service class.Comment #9
brandonpost commentedHello @apaderno, thanks so much for your review. I believe all the issues you identified have now been corrected, except for one point about which I'd like to ask for more clarification.
You mentioned that some of the classes I extended are not part of the public API, and therefore cannot be extended by contributed modules. I saw in this comment on another application that you said that only classes marked with the @api tag and classes named 'Base' are part of the public API. However, when I searched the code for Drupal 10 core, I did not find any classes marked with the @api tag. Would you mind providing some examples of classes with this tag? Or would you mind providing a link to some documentation that explains which classes can and cannot be extended so that I can understand better?
My understanding has been that core classes that should not be extended are the ones marked with the @internal tag, or classes that are programmed as 'final class' (for example, \Drupal\Core\Config\ManagedStorage), but it is very possible I might be wrong about this.
Thank you again for your time.
Comment #10
avpadernoNo, the absence of the
@internaltag does not mean the class is public API.Although I have summarized what can be considered public API (what is marked with
@apior base classes), that is essentially correct.In fact, not even the public or protected class properties are public API. In the latter case, the exception is protected properties on a base class marked with
@api, or properties that are accessed with__get()or__set()(for the classes that implement those magic methods). That means, for example that I could use$object['name_of_a_property_that_really_is_protected'], where$objectis an instance of a class implementing those magic methods, without to worry in the case the code returns the value of a protected property.The only method that Drupal 10 marks with
@apiisConfigEntityUpdater::update(). That does not mean that what reported about classes/interfaces/methods marked with@apiis not true.Comment #11
brandonpost commentedYou found the same thing I did, that there is only one method in Drupal core that is tagged with @api (ConfigEntityUpdater::update()), but there are no classes with this tag. This leads me to believe that an @api tag is not the way Drupal core identifies which classes can or cannot be extended. In that case, does this mean that only classes with the word 'Base' can be extended?
I have been searching for documentation that explains which core classes are allowed to be extended by contributed modules, but I haven't found it yet. But in my search, I came across this issue for core where there is a lot of discussion by core developers about contributed modules extending the Node class, yet no one says that the Node class cannot be extended by contributed modules because it is not part of the public API. What is it about the Node class that identifies it as being part of the public API as opposed to the classes extended in my project?
The reason I extended the classes I did is to avoid a lot of code duplication. Those classes already do a majority of the work, but I just needed to tweak their behavior in a few ways. This is the beauty of OO programming in general and of Drupal in particular. The fact that Drupal's classes are developed in such a way that contributed modules can override almost any aspect of its behavior is a true testament to its genius and one of the main reasons I love working with it.
If you know of some documentation that clearly explains which core classes are allowed to be extended and which are not, I would truly appreciate if you could give me a link so that I can understand this point more clearly.
I really do appreciate all the time you dedicate to working with these applications for the security coverage role. Thank you again.
Comment #12
cmlara@brandonpost
https://www.drupal.org/about/core/policies/core-change-policies/bc-policy Is the backend “API” policy.
Short version: very little of Drupal’s code is actually considered API, a significant portion of code is @internal by default. Even for code(interfaces) that is API in one scenario(caller) may not be API in another scenario (implementer).
Comment #13
brandonpost commented@cmlara, thank you so much for that. That is exactly the documentation I was searching for but couldn't find for some reason (I guess I wasn't using the right search terms). I see now the documentation for the @api tag that @apaderno was referring to, which I hadn't been able to find documented before.
I think the most relevant part of this documentation for the current discussion is this section:
I believe the last caveat of that statement is where my module falls, and I am prepared to update my code if anything changes within the core classes I have extended.
The thing about this module is that it is communicating extensively with a remote SQL database. However, it does not have direct access to that database, but rather must submit query strings to DonorPerfect's API and receive the results returned from the API. This issue about query strings is something that @apaderno caught in his review, saying, "SQL queries are not built concatenating strings. Placeholders must be used." In a normal situation, where the module has direct access to the database, I agree completely with this statement, as building SQL queries through string concatenation is an absolute security threat vector. But in this case, where query strings are the only means this module has to access the database, using string concatenation becomes a necessary evil. It means that the strings being concatenated must be thoroughly validated, which this module tries to do. This is also the reason why either of the permissions that a Drupal user must have in order to use any of this module's functionality are both set to 'restrict access' true.
Back to the point about extending core classes, the core developers have done so much amazing work building tools to compile SQL queries. All of those tools are extremely useful for this module since it is communicating with a SQL database, but it just has to make a few modifications so that the result is a compiled query string that can be submitted to DonorPerfect's API rather than directly to a database server. This is the reason I have extended core's SQL classes rather than duplicating all that code, and I am prepared to update my code if there are changes to core's SQL classes in the future.
I hope this provides more clarity about what this module is doing, and why I took the approach I did. Thank you.
Comment #14
avpadernoExcept that the quoted part applies when nothing else in the page applies.
If only interfaces can be relied on as the public API, then I could not access a protected property via
__get(), nor set it via__set(), since those methods are not defined in an interface, contrary to what the following sentence says.The part you quote does not even limit what said in other parts of the page. It is not that controller classes can be used as parent class only when they implement an interface; they cannot used as parent class in any case. Similarly, it is not that classes that implement a plugin can be used as parent class when they implement an interface; they cannot be used as parent class in any case.
If the PHP and JavaScript classes part were always applicable, other parts in that page should not be present.
Comment #15
avpadernoThe class in src/Controller/ControllerBase.php contained code that was not necessary; the class in src/Controller/DonorPerfectController.php still contains code that is not necessary, especially because it defines a property that is already defined from the parent class. That is duplication too.
Comment #16
avpadernoApart from the parent classes, there are other changes that have not been done.
Comment #17
brandonpost commented@apaderno, I'm sorry, we seem to be having a miscommunication that I hope we can clear up. In comment #13, I quoted the # PHP and JavaScript Classes section from the documentation. In comment #14, you seem to focus on the first line of that section which says, "In general, only interfaces can be relied on as the public API." Please know that I understand and agree with everything you said about interfaces in comment #14. The point I was trying to make in comment #13 was not related to that first line about interfaces at all. After that first line, it goes on to say that core classes might change, and therefore developers "should not inherit from classes in the system, or be prepared to update code for changes if they do." This does not seem to be setting a hard line rule that core classes can never be extended in any case. Rather, it seems to be saying that, in general, core classes should not be extended, but if a developer does extend a core class, then that developer should be prepared to update his code if the core class changes.
In comment #13, I presented my reasoning as to why I extended the core SQL classes, and I stated that I understand and am prepared to update my code in the event that the core classes change in the future. I believe this aligns with what the documentation says about extending core classes. But if you do not agree that my reasoning justifies extending the core classes in this case, please let me know and I can work from there.
Moving on to comment #15:
I believe you are aware that this file no longer exists, but it seems you are mentioning it to make a point that I have duplicated code, even after I said in comment #11 that I extended the core classes in order to avoid duplicating code. Please let me explain. In comment #8, you said that my ControllerBase class contained a
container()method that was already in the parent class, and was therefore code duplication. However, in the parent class thecontainer()method was private, whereas I overrode it to make it protected, so that it could be accessed by my subclasses, and therefore it was not actually code duplication. You also said that myconfigFactory()method was unnecessary since the parent class already had aconfig()method. However, theconfig()method in the parent class only provides immutable config, but I had a subclass extending my ControllerBase that required editable config, and thus the need for theconfigFactory()method. Therefore, I disagree that my ControllerBase class contained any duplicated code. However, based on your feedback, I refactored my code to do away with the ControllerBase class completely, and I think the module is better now because of that, so I thank you for that.Here again it seems you are making a point that I have duplicated code after I said that I extended the core classes in order to avoid code duplication. But again I disagree that there is any code duplication here. I believe the property you are referring to is the
$containerproperty. But the parent class actually does not have a$containerproperty. It does have acontainer()method, but that method is private, making it inaccessible to my subclass. Therefore, I added a$containerproperty so that it could be used within my subclass.I truly do want to avoid code duplication, so please let me know if you have found any other instances, and I will work to resolve it.
Moving to comment #16 where you said there are other changes that have not been done, I have reviewed each point you made in comment #8, and I have tried to make all the appropriate changes in my code. I understand that your points in #8 were examples and that there were other places in my code where your points applied, and I have tried to correct all of those as well. The git history should show that I made significant changes to my code after your review, and I believe it is a much better module now thanks to your guidance. If there are changes that still need to be done, please give me some direction as to what they are, and I will work on them. Thank you again.
Comment #18
avpadernoNo, I focus on the fact that part applies when other parts do not apply. Otherwise, that full documentation page would be much shorter: It would need to just contain the PHP and JavaScript Classes section, instead of having all those parts about classes, interfaces, and properties.
Since the purpose of that section does not seem clear, let me ask this: Are there classes implemented by Drupal core or Drupal core modules, which are not usually used as parent class from classes implemented by contributed modules, for which the constructor is not called (not even from Drupal core), and for which none of the properties are used by a contributed module?
If the aim is not duplicating code, creating a class just to duplicate a method should not be done. Yes,
ControllerBase::container()is private, but that does not mean that overring that method just to make it protected or public is not duplicating code.Furthermore, the reason for which that method is private is documented in that method.
That is exactly the opposite of what
DonorPerfectControllerdid. I have reported that in comment #8. I reviewed the code further two times, to discover there are still changes that need to be done for that class.(Hint: Needs review is not for letting reviewers answer any question applicants have; on this queue, that status means I have changed the code as requested and the project is ready for another review.)
src/Entity/ControllerService.php
Services do not need a property containing the dependency container: Each dependency a service has is defined in the service definition. The class is also using the container for the wrong purpose: Forms are instantiated using the form_builder service.
src/Entity/Storage.php
Entity objects are created calling the
create()method.As a side note, it is correct to use
\Drupalmethods in that case, but not becauseDrupal\Core\Entity\EntityHandlerBase::moduleHandler()does it. It is because classes that implementDrupal\Core\Entity\EntityStorageInterfacecannot also implementDrupal\Core\DependencyInjection\ContainerInjectionInterface. The only way they have to get dependency injected is using\Drupalmethods.src/Form/EntitySettingsFormBase.php
$entity_type_label = ucfirst($this->getEntityType()->getSingularLabel());For a label that starts with a capital letter, there is
getLabel().getSingularLabel()takes the value returned fromgetLabel(), passes it tomb_strtolower(), and then this code passes that value toucfirst(). It is not necessary, sincegetLabel()returns strings like'Content','User','Taxonomy term', or'Taxonomy vocabulary'(just to make a few examples with Drupal core entities).src/FormValidator.php
Just to point out that method will always return an empty string.
Comment #19
cmlaraRespectfully I disagree, the section must be used in context of the other sections, since the entire page is about PHP classes this section acts as a general clarification that is binding over the entire page unless explicitly revoked.
Addtionaly the majority of that page is actually clarification for Core Committers (and core contributors) to understand the BC policy rather than direction regarding the use of Drupal's methods.
There are many should not, and no must not phrases listed in the BC policy page.
OOP standards indicate that if the Drupal Core Team is to list as a class to never be extended it MUST be final and for a property to never be accessed it must be private. Lacking final/private the it may not be recommended to do so however the extender is free to accept that risk.
The Drupal Core team has a history of not using final or private in order to allow code to be extended even if it is off API (see SLACK #contribute there were recent discussions on this regarding final and Access Policies). Furthermore the Drupal Core Team routinely and intentionally goes out of its way to maintain BC with non API classes, methods and properties (to the detriment of Cores code base) under the assumption that non API classes will be used directly.
Extending a non-final class is not on its own a sign of insecure programming or misuse of the Drupal API, only that the program has accepted additional support burden.
More importantly, IIRC this queue has approved applications extending non API classes in the past year.
That said to the applicant:
My suggestion is to not extend core classes, and do not use ServiceContollerBase. Extending classes will just make life more painful and tie your code to specific patch releases of core while ServiceControllerBase breaches the standard of Dependency Injection. Both of these make your code brittle and harder to test. Code duplication is not always bad.
I have not reviewed the code in detail, this message is solely in response to @apaderno's assertions regarding the BC policy and its impact on Code Review.
Comment #20
brandonpost commentedI have changed the code as requested and the project is ready for another review.
@cmlara, thanks a lot for the advice. I followed it in this case, and it will continue to be helpful moving forward.
Comment #21
vishal.kadamI am changing priority as per Issue priorities.
Comment #22
avpadernoExactly, but that means that if a section says that the parent class can only be a base class, what follows must be read considering that part.
As for that policy, it clearly states:
Reviewing for the correct Drupal API usage, I consider using as parent class a class that is not a base class a not correct usage of the Drupal API.
I also asked a question in my previous comment, but that has not been answered from the applicant.
Comment #23
avpadernoThe only time I recall I did was because Drupal core did not define any base class that should be extended. That is not the case here.
Comment #24
brandonpost commented@avpaderno, please note that in my code I have removed all extensions of core classes. I have completely accepted your position. Most of what you said in comments #22 and #23 was in response to points raised by @cmlara in comment #19. I am happy for you and @cmlara to have this discussion, but at the same time I would also like to know what I need to do in order to complete this application.
If I understand correctly, then answering this question is the final thing blocking my application, is that right? From what I can tell, that appears to be the reason you changed the status back to 'Needs work'.
To be honest, I thought this was a rhetorical question, which is why I didn't answer it. Also, the English syntax of the question is fairly complex, so I'm not sure I completely understand it. The only class that immediately comes to my mind that I think satisfies all the criteria in the question is the \Drupal class. Here is my reasoning based on a breakdown of the question:
Perhaps there are other classes that meet these criteria, but I am not able to think of them at the moment. If this is not the answer you were looking for, please re-ask the question in a different way so that I can understand it better, and I will give another attempt at answering.
Comment #25
avpadernoThe
\Drupalclass, like the\Drupal\Component\Utility\Byte,\Drupal\Component\Utility\Color,\Drupal\Component\Utility\Crypt,\Drupal\Component\Utility\DiffArray,\Drupal\Component\Utility\Environment, and the \Drupal\Component\Utility\Htmlclasses, just to list some of them, defines only static methods. It means there is a set of classes for which the following text applies and applies only to them.Those classes do not implement interfaces, and they are not base classes. Basing on the rest of Drupal core backend backwards compatibility and internal API policy (Drupal 8 and above), those classes should not be used. The quoted text is saying Maintainers can use them, but be prepared to update their code because of changes in those classes.
As a side note, Drupal 12 will rename
\Drupal\Core\Render\Element\FormElementto\Drupal\Core\Render\Element\FormElementBasemaking clear it is a base class.(See also RenderElement and FormElement base plugin classes are deprecated and renamed to RenderElementBase and FormElementBase.)
Comment #26
avpaderno