Problem/Motivation
Through the Update module, Drupal core informs administrators about currently available security releases that affect their site. But it does NOT display any information about upcoming security releases. This means that site admins must check outside of Drupal to know whether a security release is forthcoming. Also, Drupal core does not indicate the severity of the security releases that it shows.
Very rarely, there are highly critical security releases with the potential to affect so many sites that it would be good to notify site admins from inside Drupal.
Proposed resolution
As part of the Automatic Update initiative (see #3039120: Create initial feature to display relevant PSA data in Drupal), the Automatic Updates contrib module displayed public service announcement (PSA) and security advisory (SA) messages, fetched from https://updates.drupal.org/psa.json, inside Drupal itself. This feed, created in #3068539: Add psa.json API endpoint to support automatic updates, is used by the security team for critical security announcements and was intended for consumption by Drupal core. The feed is documented at https://www.drupal.org/docs/8/update/automatic-updates#s-public-service-.... We propose to add functionality in the System module to notify site admins of PSAs and security advisories that appear in the feed.
In #3196368: [policy, no patch] Determine to which module the new security advisory functionality should be added, the System module was chosen to host this functionality instead of the Update module.
Sites admins will ultimately be notified in 3 ways:
- On the status report page at
admin/reports/status. - On certain other administration pages (for users who have the 'administer site configuration' permission).
- Via e-mail, which will be implemented in #3197333: Email site admins for security advisories displayed in Drupal.
We follow this logic to determine if a feed item is relevant:
- If it's about an extension that is present on the site (not necessarily installed), or about core itself. For PSAs, this criterion alone makes the item relevant. Otherwise...
- If the extension's exact version is known, and listed in the item's
insecurefield. - Or, failing that, if the site's version of the extension is a development snapshot that specifies a major and minor version and both these match for a version in the
insecurelist. - Or, failing that, if the site's version of the extension is a development snapshot that specifies only a major version and it matches for a version in the
insecurelist.
In other words: for a given extension, we'll try to match on the exact version, then fall back to just the major and minor, then fall back to just the major version. If the item is not a PSA and we can't determine the version at all, we don't consider it relevant.
See https://git.drupalcode.org/project/drupal/-/merge_requests/284/diffs#bb5... for where this logic is implemented in code.
Remaining Tasks
Documentation
- Main documentation, needs review: Highly Critical Security Advisories within Drupal
- Needs to be updated to mention the fetching of advisories: PHP OpenSSL requirements
Questions & Answers
Do we need a way to turn this off?
Yes, but it's not something you can change in the UI (like the contrib module has). It should be very rare for anything to appear in the feed, so most of the time (we'd guess 95%), if a site admin disabled this feature, they'd see no change in their site and it wouldn't be clear what the setting did or why they'd want to toggle it. But the consequences could be very bad if they turned it off and missed a critical security update. That said, there are many cases where security updates are monitored outside of Drupal, so it makes sense to have some developer-facing way to turn it off (we have implemented this with a config setting).
Should we follow the same logic as the contrib module?
We show all PSAs that refer to a project which is present on the site. It does not have to be installed; the admin should be made aware of the risk of installing, and certain risks don't require the module to be installed. For non-PSAs, we also need to match a version that is listed in the item's insecure field, according to the logic detailed above.
The contrib module currently displays all PSAs in the feed where the project is core, or installed on the site. For non-PSAs, the project's version must also be listed in the item's insecure field.
So we're pretty much doing what the contrib module does.
How might this change in the future?
In #3068539: Add psa.json API endpoint to support automatic updates @drumm said:
The planned use of this API is only for highly critical updates which we hope as many Drupal sites as possible set to automatically update. It does not include all PSAs.
We should get clarity if the feed could change later to include anything other than highly critical updates. If it could, then versions of core that were still in use before core changed its logic to handle this would start to show other updates.
Currently there is nothing in the feed that specifies security risk level. We could ask for this to be added to the feed properties. Then we could filter for it now even though no other updates should be in the feed. This way if they were added later they would still be filtered out.
Should we show PSA's for projects that are not installed?
Yes. The contrib module does not, but we want to, because:
- Certain remote code execution vulnerabilities might not require the module to be installed.
- A module could be installed at any point. A site admin should probably know that a critical security release is forthcoming in the next week so they can make an informed decision if they want to install a module or maybe wait until the release.
Follow ups
- User guide issue: #3203748: Add information about highly critical security advisories within Drupal
- #3197333: Email site admins for security advisories displayed in Drupal
- Make a list of improvements and bugs found here that should be backported to the contrib module.
See #3201183: Consider fixing these issues found when porting PSA functionality to Drupal core- After this is released in core update the contrib module to not display PSA's if the core version install will display the PSAs. This may just meaning checking for the existence of the services added in this patch
- 🔥🔥🔥 Critical Assumes extension name and project name are the same. See #42.1
- change
getPublicServiceMessages()to throw exceptions rather return error messages as PSA messages are returned. change the callers to react to the exception and change what is displayed for an error. - Change to handle invalid versions of installed modules see #37.7(edge case)
- Edge case: handle individual
insecureversions that don't parse #37.6 - Throw validation error if notifications are set for PSA and no email is provided for the update module(contrib module doesn't have email setting of it's own), @see #43.2
- Don't send the same PSA email multiple times @see #43.3
- Added tests for invalid json
- SecurityAnnouncement class with extra validation and getters
enable_psasetting in the UI is described in contrib module as "Show Public service announcements on administrative pages", but if unchecked it will also stop emails and the status report.
API changes
- There's a new service in town,
system.sa_fetcher. This is a final class. Code can call to retrieve the advisories that are relevant for the current site. See #137 for why the class is final. - New config settings object for the System module
system.advisorieswith 2 settingsenabled: This controls whether the advisories will automatically be fetched during cron.interval_hours: This determines how often advisories will be fetched
User interface changes
Critical security advisories and public service announcements will be displayed on the status report page and certain admin pages to users who have 'administer site configuration' permission.
Status report page:

Messages on admin pages
1 PSA: displayed as warning because you can't update to specific version to remove this message

2 PSA: less likely there would be 2 items but this is what it would look like

1 non- PSA: displayed as a error because there is release to update to right now

Data model changes
None
Release notes snippet
Certain highly critical security advisories (SAs) and public service announcements (PSAs) will be displayed on the status report page and certain admin pages to users who have the 'administer site configuration' permission. Examples of past announcements that would have been included in this new feed:
- Critical Release - PSA-2019-02-19
- SA-CORE-2019-003 Notice of increased risk and Additional exploit path - PSA-2019-02-22
- Drupal Core - Highly Critical - Public Service announcement - PSA-2018-002
While it is recommended to leave this functionality enabled, sites that do not want these advisories to be fetched from Drupal.org can add this line in settings.php:
$config['system.advisories']['enabled'] = FALSE;
| Comment | File | Size | Author |
|---|---|---|---|
| #206 | advisory-1-non-psa.png | 20.85 KB | tedbow |
| #206 | advisories-2-psa.png | 18.59 KB | tedbow |
| #206 | advisories-1-psa.png | 19.16 KB | tedbow |
| #203 | help-page.png | 239.32 KB | webchick |
| #203 | errors-on-errors.png | 195.25 KB | webchick |
Issue fork drupal-3041885
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #4
heddnComment #5
gábor hojtsyMy understanding is that https://www.drupal.org/u/beautifulmind is working on this patch. I cannot assign because they are not a maintainer.
Comment #6
beautifulmindChanges have been committed and here is the patch. Please review and confirm.
Comment #7
dwwUpdating status based on the patch being uploaded. Will review when I can.
Thanks!
-Derek
Comment #8
longwave$tocomes from config, but this won't send a mail if the email address doesn't have an account.Also, the loop over $users isn't right,
$userisn't even used inside the loop.Finally, the first parameter to
$this->mailManager->mail()should be the module name, "update_psa" isn't a module.Comment #10
beautifulmindThank you for the review. I will do another iteration by fixing all the issues and will submit for the community review.
Regards.
Comment #11
tedbow@beautifulmind thanks for getting this started!
Not a full review but a few things
I think these settings can just live in
core/modules/update/config/install/update.settings.ymlBut we actually don't need most of these. We only need the settings that relate to the actual PSA functionality. These are all the settings for the contrib module but we are not adding all of the functionality here.
Same here. We only need the schema for PSA functionality. probably just the first 4 items. I think we can nest these for under the existing schema in the file under their own collectoin "psa"
Not used. Don't need a use statement if only referenced in a comment.
The class name should just be
UpdatesPsabecause we are adding this to the Update module directly.Also the class needs description of what it does instead of the current comment
This dynamic accessing of the properties was fixed in #3157857: Do not accessed Extensionlist class variables in AutomaticUpdatesPsa dynamically
@beautifulmind were working from the 8.x-1.x branch of the contrib module or 8.x-2.x just before this was commited? either is fine because I don't think there has been that many changes to this functionality.
but I think we should bring in the changes from that issue.
I think this patch would be easier to review if we did the mailing in a follow up.
but it is probably a product/release management question as to whether we can have the notification only displayed on the screen as first step.
Need a class description
Need an actual description
Should change to
:linkinstead of@linkbecause it is in html.But actually this shouldn't be link because it links the contrib module docs.
Not used
trailing space
trailing space
Comment #12
aaronmchaleWould someone who is "in the know here" be able to provide a concise and clear summary of what's trying to be accomplished in this issue? I'm struggling to understand how exactly this will differ from what's already in Core (as in, Core already has update status screens + update status emails).
Thanks,
-Aaron
Comment #13
beautifulmindHello @tedbow
Thank you for the detailed review. I noticed those points and will make enhancements as well.
Initially, I wanted to combine those settings but for the sack of clarity, I created a separate file under /install!
In the next iteration, most probably by Monday evening or Tuesday morning, I will enhance the code further and submit a patch for community review.
Once again, I really appreicate your detailed review & feedback. Thank you. :)
Regards.
Comment #14
beautifulmindAll the issues have been fixed, logger added, PSA Notification message template added. Fixed all the Coding standards issues. Please review the patch.
Regards.
Comment #15
beautifulmindComment #16
xjmMarking NR for #14.
Comment #17
beautifulmindI just cleaned the files for white spaces. Should I upload the cleaned patch with same file name? Or should revise the file name with target comment number? Please advise.
Regards.
Comment #18
naheemsays commentedNot a core developer, but new comment number is always better (you can track just the whitestpace changes in an interdiff.txt or interdiff-oldno-newno.txt).
Comment #19
beautifulmindHere is the cleaned patch.
Regards.
Comment #20
dww@beautifulmind Thanks for uploading that. Would you be willing to also post interdiffs with your patches, so folks can see what's changed between each version?
Thanks!
-Derek
Comment #22
beautifulmindCertainly! I will post the interdiffs as well.
Comment #23
beautifulmindDidn't mean to change the status.
Comment #24
beautifulmindHere is the interdiff.
On a side note, when I tried executing a test from CLI on my local environment, I accidentally deleted everything under /core, losing my work! But then I had the patch created just before that. I ran composer update, and then applied the patch! I have everything back!
But here, it keeps failing, throwing different kinds of errors. Any thoughts?
Comment #25
beautifulmindI figured it out!
Comment #26
tedbowWe no longer need this logic because the only class that uses this trait is
UpdatesPsaand it should always have$this->extensionLists[$extension_type]set. If not should throw the exceptionthis can be remove too
This comment I think refers to the readiness checkers. This is copied directly from the contrib module but it looks to me like the contrib module had this comment on the wrong line.
Unrelated change
Unused
It doesn't make sense to just add all the new services to a "Services" namespace. The update module already has a punch of services. I think we should create a new "Psa" namespace
I didn't review them yet but I wanted to bring them to make sure this is working. I had to make some more changes to make the tests pass.
Comment #28
longwaveWe can refactor away this check, because this will always be true.
Comment #29
beautifulmind@tedbow Thank you for the suggestions. I fixed all the issues and will remove unused code and comments.
About the readiness check comment, I kept it thinking it will help implementing that feature when we arrive there. But for now, I will remove it.
@longwave Thank you for the suggestion. I will accomodate this in the patch.
Comment #30
tedbow@beautifulmind sorry I was unclear I fixed the issues I commented on. See the interdiff file that I included with #26
I removed that but also the readiness checks are being worked on here #3162655: Create Automatic Updates Readiness Checks in new experimental module. They are being implemented in a new experimental module.
UPDATE: sorry I guess I didn't actually remove this comment
Comment #31
tedbowThis patch adds 2 things
Comment #33
beautifulmind@tedbow Thank you for the update and clarification.
Regards.
Comment #34
tedbowWe should consider expanding
\Drupal\update\ModuleVersionto handle this parsing.We added this class we intentionally made cover only the parsing we needed at the time and made
@internalBut since this is still in the Update module it would not affect the @internal status.
UPDATE: actually this is dealing with versions in
$json->insecurewhich we can't rely on.So probably don't need this whole method.
I think the logic here is wrong.
looking at the docs for the the PSA feed and the
insecureproperty it saysSo in the code we are only adding messages if current version is within
insecure. We should not be filtering by this.By policy for security releases the versions that will be affecting cannot be disclosed before hand.
So I don't think we actually need this at all.
UPDATE: so reading this code again I see it only matches the version if it isn't a PSA. I am not sure what other types of message are going to be in the JSON fee but currently the docs don't say the
insecurechanges for non-PSA's. I have pinged @drumm and @heddn about this.insecureI am going to assume the current functionality is correct.I still think the logic here is confusing.
First we call
$this->isValidExtension($json->type, $json->project)2x. I think we should just call it once an at the top of this block and justcontinueif it is FALSE.2nd,
parseConstraints()doesn't really "parse constraints". $json->insecure just as an array of versions only internally inparseConstraints()does it turn them into constraints, so from the caller perspective the name doesn't make sense. Also it calls\Composer\Semver\VersionParser::parseConstraints()which uses the same method name but does actually just parse constraints.3rd in addition
parseConstraints()also adds to the$messagesarray which is not clear at all reading this code block.4th,
contribParser()has the same problem as as above but also determines the installed version of the project. So it does 3 things.I have refactored this to have 3 methods,
matchesInstalledVersion()which will callgetContribVersions()andgetInstalledVersion()I think this will be clearer because the 3 methods can be clearer in what the do.
Then this code block can just be
To me this makes it more clear that we add messages for all PSA's and for other types we only add the message if it matches the installed version.
I wasn't clear on this before.
Comment #36
tedbowis_psa !== trueLeft this is because I was testing something. Fixed
Comment #37
tedbowMore review
Since the the method is
toSendI don't think we need to call this$to.$emailseems better.We don't need the throws if the method itself don't throw these. At least that seems to be the pattern in core.
The
$uservar is never used. Also there will always at most be 1 user matching an email. So we don't need the foreach loop.This class just accesses the properties $json directly with no documentation of what will be in the object.
I have made a SecurityAnnouncement class that checks for the keys in its factory method throws an exception if the expected keys are not found.
Before the changes in this comment the only place this could be thrown is in calls to
\Composer\Semver\VersionParser::parseConstraints(). i have checked the contrib and this is the same.This means the exception could be thrown if an item in the JSON has a bad value for
insecure. If so then no items will be displayed. This seems very unlikely to happen because the PSA feed from drupal.org is unlikely to have this error.But in the unlikely even that this does happen where there is problem with 1 item do we want to not show any items in the feed?
Since these at first will be used with highly critical updates we should probably error on the side of showing as much information as we can.
There seem to be 2 options for handling items that will have this problem
1) Just don't show the individual item that has the problem but show the other items.
2) If the item's only problem is in the parsing of
insecureassume when can't parse this that we should show the message. Because it would be better to show the user a highly PSA message that does not apply to them then to NOT show them one that doesn'tWe concatenate the version into 1 constraint string here so that we can call
\Composer\Semver\VersionParser::parseConstraints()one time.but this means that if any of the version strings are malformed an exception will be thrown.
I also don't think think calling
parseConstraints()1 time vs once for every version will actually be much of a performance gain. InternallyparseConstraints()will call itself recursively for each version anyways. It will probably actually be worse performing because it has to parse the string to get each version.Changing to call once for each version.
This would throw the exception if the installed version has an invalid constraint. Not sure how this could happen but maybe if the extension was patched or the extension was installed from a git clone.
This could result in not showing a PSA because the installed version number has a problem.
Again I think here we should error on the side of showing the PSA when we can't be sure.
These 2 settings don't work they way they are described currently.
Because
psa.enableis checked in\Drupal\update\Psa\UpdatesPsa::getPublicServiceMessages()and an empty array is returned if it is empty then this settings actually everyone where the messages might be shown, 1) on admin pages, 2) on the status report and 3) via email.I am changing
psa.enableto only be checked inupdate_page_top()so that it only affects showing on admin pages like it is described here.You should be able to not show the messages on admin pages and still send emails, which the current functionality does not allow you to do.
I think showing on the status report should not be able to be disabled. I don't think we allow that for available updates except by disabling the Update module.
The form setting didn't actually work here I think because of the period. The tests still passed because we were setting directly instead of using the form. I have fixed the form and changed the test to use the form.
Comment #38
tedbowOn the status report page: It will display "1 urgent announcement requires your attention" and then "Drupal PSA JSON is malformed." in the list
In the notify email:: The same behavior happens.
This is because in
\Drupal\update\Psa\UpdatesPsa::getPublicServiceMessages()the exceptions are caught and "Drupal PSA JSON is malformed." is returned as the only message. So no caller has a good way to tell that an error occurred.I think a better solution would be for
getPublicServiceMessages()to throw the exception and the callers handle it.I will fix this in the next patch.
Comment #39
tedbow\Drupal\update\Psa\UpdatesPsa::getPublicServiceMessages()throws exception rather than returns the error message in the announcement messages array. see #38.3This means the callers can respond to the exception. I updated the tests but
\Drupal\Tests\update\Functional\PsaTest::testInvalidJsonEmail()will fail. I am not sure what do in this case. Should we send an email saying we couldn't fetch PSA information and the admin should check drupal.org? or should we not send an email and just wait until it can get valid PSA'sHere we check if should actually send the messages via email based on
update_psa.notify_last_checkbut we have already calledgetPublicServiceMessages(). If we aren't going to send the emails there is no reason to call this.Moving this to the beginning of the function.
Comment #41
tedbowWhen calling here to send an email I decided to just log an error if the message could not be retrieved.
This was happening in hook_requirements
updatephase so it was causing test to file. This only should happen in theruntimephase.This same message was being constructed in 2 places and now a third that am handling errors when trying to send an email. I create a helper static function
\Drupal\update\Psa\UpdatesPsa::getErrorMessageFromException(). This can be used in callers in get the message to display.Comment #42
tedbow🙀I just realized that this logic is assuming the
projectvalue that comes back in the PSA feed will be the same as the extension(module,theme,etc) name. This is usually true but not always. The project name is short machine name that is on Drupal.org. It does not have to be used for the extension name.changing to use the UpdateManager instead since it returns an array keyed by project name. This is more expensive but since this is not called often it should be ok.
projectdoes not match the extension name.I realize now the reason we can't use the json is because we can't hardcode for the core matching version. As it is now this would fail in 9.2.x. Changing back to use the controller.
We could use the JSON if we dynamically set the core version which is what we use in other update tests. But changing to use the controller for now.
Comment #43
tedbowThis should be checked at the beginning of the method instead because we can't do anything if we don't have any emails. Fixed
This checkbox can be checked even if there are no emails provide. Adding validation to prevent this.
$notify->send()should not be within this if statement. Here$frequencyis fromcheck.interval_dayssetting which is how ofter to check for updates not PSA's.We could take it out of the loop and just call it every time cron runs because
\Drupal\update\Psa\EmailNotify::send()already checkspsa.check_frequency.but this still is not great because that means that if items stay in the PSA feed for more than 12 hours the admin would be sent the same message every 12 hours.
So it think
EmailNotifyshould not check how long it has been but checks if the last messages it sent are different than the messages it got back from\Drupal\update\Psa\UpdatesPsa::getPublicServiceMessages()getPublicServiceMessages()is already using a cache so it won't check that often.Changing
testPsaEmail()to test that a email doesn't be sent if the messages have not changed and that the email is sent when the message does change.Using
\Drupal\update_test\Datetime\TestTimeto test the cache expires and the feed is checked again. But until #3113971: Replace REQUEST_TIME in services fixed this will not work. So copying just the changes to\Drupal\Core\Cache\DatabaseBackendthat are needed for now. Hopefully we can get that issue committed soon.Comment #44
tedbowComment #46
tedbowComment #48
tedbowUpdated summary with a list of improvements made here from the contrib module that should be considered for the contrib module.
The project name !== extension name is probably the most critical
Comment #49
tedbow\Drupal\update\Psa\UpdatesPsa::getInstalledVersion()get the core version that way it does for other projects from info returned by\Drupal\update\UpdateManagerInterface::getProjects(). This how the Update module currently determines the core version to determine if there available updates.This allows using actually json files instead of the controller class to return the json feed because we can alter the version of core like the patch does with other modules. This is also more like using the XML test fixtures that the Update already use.
This should make the tests pass.For some reason the tests don't pass on drupalci because the mocking of the date to trigger the json PSA feed to be fetched again when the cache expires. This works locally but not on drupalci. If anybody could see #43 or #46 pass locally for them that would be be great.
So I have changed the test to manually delete the
updates_psacache to trigger fetching of the PSA.Probably we don't need to do 1) to make the tests pass but I think using actual json files is better anyways. Also getting the core version the same way we do for other projects and the same way that other parts of the update module do is better.
Comment #51
tedbowAfter a ton of more debugging it seems this line is the problem.
I looked at
\Drupal\update\UpdateFetcher::fetchProjectData()I noticed it did not callgetContents()but instead casts the result ofgetBody()to a string.I am not sure why but I am guessing because
\Psr\Http\Message\StreamInterface::getContents()says itSo if it has been called before you have to call
\Psr\Http\Message\StreamInterface::rewind()set it back to the beginning of the stream. Not sure why that would be different on drupalci.@longwave and @laurii also tested by debug patch and they saw the same fails.
Added a @todo here for #3113971: Replace REQUEST_TIME in services so that we can update this test to just mock the date and test the cache gets cleared
Comment #52
tedbowupdating the summary
Comment #53
xjmLet's review this in the UX meeting next week.
Comment #54
tedbowComment #55
xjmThoughts on the points in the IS:
I don't think we need this setting. The announcements are rare as it is and less config == better.
I agree using
notification.emailswould make sense (rather than a new setting) for the reasons mentioned.Let's file an issue for this against d.o. Sometimes the PSA is about an upcoming release, but not always. And I agree for UX reasons we would ideally stop displaying the message once the user has updated to the version that addresses the issue. I think this could be a followup.
Per @drumm the only way these items get into the feed is via a Drush command that someone with prod access on d.o runs. I think that's extreme enough that we should display everything in the feed for a project in the site's codebase. I asked @drumm if it could be a UI config option on PSAs, but even then, we should trust the sec team's judgement that the item is worth displaying on the status report. PSAs are not typically assigned a severity so I don't think we need extra logic for that.
I agree that we should display the PSA for anything in the site codebase, installed or not. As discussed, even uninstalled modules can expose the site to security risks for certain vulnerabilities. I can think of several such vulnerabilities in the past 5 years.
Comment #56
xjmI think NW to address points 1, 2, and 4. There are also a number of CS errors:
Comment #57
xjmI believe our coding standards specify a newline between the opening curly and the first line here (although it didn't fail phpcs).
Inane observation: That's a lot of services.
Can we add return typehints where feasible?
Non-blocking: This seems like it is probably code repeated from elsewhere and probably should be API or internal functionality for the messaging service rather than hardcoded logic in the caller.
I think we have a practice of not putting the word "Drupal" in the UI anywhere since it can be branded as something else. Should we say "security announcements"?
Can you guess what I'm going to say? ;)
As always, let's use a more specific data type for the
arrayif possible, and describe and/or reference the canonical array structure for associative or mixed arrays.Also, no apostrophe in "PSAs".
Nit: there should not be an apostrophe in "PSAs". (Here and elsewhere in the patch where it's a plural rather than a possessive.)
I think they are violation messages, not volition messages? The words mean different things.
Blah blah start with a verb etc.
Leading space.
What module's configuration?
Nit: "HTTP" should be uppercase in code comments.
Interesting. This seems like it should be stored in state rather than cache? Otherwise a cache clear blows it away and resets it.
Also, we seem to be storing prognostication about when we should check next, rather than when we checked last. I'd expect the other way around, with a check on the other side to decide whether it's long enough ago relative to the config.
However, we should primarily mimic whichever pattern the update module already does when checking for updates.
Logger and an exception? Do we really need to log messages about malformed PSAs? The site owner can't do anything about that.
Come to think of it, should we even be throwing an exception? (For the same reason.)
Same question about the use of the logger.
Nit: "PSA" should be uppercase when used in a sentence.
An SA is not the same thing as a PSA. I think the parameter name should be
$psa.Nit: Comma splice. Either the word "or" or a semicolon would resolve this.
Nit: "cannot" should be one word. (MURIKA!) Also, there should be a comma before "assume".
Let's get UX review for all this text.
Also, I think we should not use the abbreviation "PSA" in the UI. We should always say "public service announcement".
approve
Why are these versions in the list twice?
I know this is only test data, but the PSA title will always have a full date, not just a year. Otherwise we could only issue one per year.
🤔 Do we need to override the default? (We already made Stark the default test theme in D9, right?) Does the theme even matter?
Nit: I think this is supposed to say "A test PSA endpoint" (not "A test end PSA endpoint").
Missing space between colon and typehint.
Inane observation on test data: Hot damn, 9.11.0. This is a version that should never be released. 🤞
Not really a sentence.
Nit: "Set up" should be two words.
I don't think we should display any message if there are no PSAs as it would be non-actionable clutter on the status report. We should validate this in the UX review, at least.
Comment #58
tedbow@xjm thanks for the review!
re #57
\Drupal\update\Psa\UpdatesPsa::getErrorMessageFromException()because it returnsTranslatableMarkup|string\Drupal\update\Psa\UpdatesPsa? This is where$messagescomes from.In this case I think it only makes sense for the email context not send the messages again. The other callers of
\Drupal\update\Psa\UpdatesPsa::getPublicServiceMessages()probably still want to display the message even if they have been displayed before.Here the intention really is "don't email the same messages 2x". Only the caller here knows if they have been emailed.
arraytype hints to more specific such asstring[]\Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface::setWithExpire()to store available release on a per project basis in\Drupal\update\UpdateProcessor::processFetchTask()Changing to use that here but simpler because we are only dealing with 1 feed.
I still think we should throw the exceptions. Maybe in the email notification and the displaying on the admin we don't need to say anything if the feed is malformed but I think on the status report we should. Hopefully this feed will never be malformed but if it is it also could be the case that there is something wrong with the fetching of json file on the clients network and not a problem with drupal.org. If we don't display a message on the status report the site admin would never know this and if I PSA actually was in the feed they won't get it.
Also I think any caller of
\Drupal\update\Psa\UpdatesPsa::getPublicServiceMessages()should be able to tell the difference of between a feed with no items and problem with the feed. Without the exception this is not possible.is_psais false because it is called for viamatchesInstalledVersion()from hereBecause if it is a PSA we don't care what the installed version because as the feed currently works
insecurewill not tell use for a PSA whether it will affect the installed version when the fix is actually released.Leaving as
$safor now.assigning to myself to address #55
Comment #59
tedbowwhoops here is the interdiff
Comment #60
tedbowback to #55
Comment #61
tedbowTaking care of #55.5 showing messages for extensions that are not installed.
This meant we can't use the UpdateManager to get projects because it caches the results and doesn't get uninstalled extensions unless the setting is enabled.
So had to change to not returning the test json files directly because we can't change the version of number of Drupal core. So added test controller to swap out the core version dynamically in the test json fixtures.
This should have been 'module'. Didn't catch this before because
\Drupal\update\UpdateManagerInterface::getProjects()returned all projects regardless of type and we weren't checking.Comment #62
tedbowSome more self review
This should be
string[]Here all we care about is the project version so we can remove this and just call the new
getProjectVersionWe no longer are just worried about installed extension. Also here we actually are getting version constraint that we can compare. Changing the name to
getExistingVersionConstraint()In the places this is called all we actually need is the project version. So changing this to
getProjectVersionand returning an empty string if it is not available.Comment #63
tedbowThis will only send an email if there is matching user. This is different than what
_update_cron_notify()currently does. Changing to still send the email with the default language code if there is no user for the email.Removed the rest of the changes to the settings form since we have no new settings now
I just realized that we should be showing these messages on all admin page except the status report. We were excluding the same pages as available updates but that list for exclusion is based on where the user maybe seeing or uploading new updates.
Comment #64
xjmMoving screenshots to the right section of the UI.
Comment #66
tedbowWording changes from the UX meeting.
Comment #67
xjmFixing the sample email to not reference links that got removed.
Comment #68
tedbowComment #69
benjifisherComment #70
benjifisherOops, I meant to decline credit, not assign to @tedbow.
Comment #71
dwwFinally had a chance to closely review this. Mostly looking good. This is going to be a big improvement! Thanks to everyone who worked on this.
As is often the case when I review a huge patch, tons of pedantic nits, with a few items of substance...
PSA is an acronym. What's the deal with namespaces and directories in this case? I guess core's "JsonApi" is an example of CamelCasing something that "should" be JSONAPI, so I guess this is fine. ;)
This doesn't (really) have anything to do with automatic updates, right?
Curious why 'Updates' is plural here (and everywhere). We're adding this code to the 'Update Manager', not 'Updates Manager'...
Every other one of these var comments starts with 'The'.
I'm probably tired and wrong, but it seems like the ['@error'] array is in the wrong place here. It's the 2nd arg to error(), not t()...
Do we want to call getStorage('user') once outside of this loop, instead?
Reading this patch from the top down, so maybe this is answered later, but I'm not clear why we need an interface for this. Are we expecting this to be a public API and contrib / custom are going to implement their own versions of this interface?
s/form/from/
a) s/announce/announcement/
b) s/is PSA/is a PSA/
c) What other types of announcements are there? I'm not clear when this is ever FALSE.
Pedantic aside: I still keep doing double takes on these. ;) Seems like isPSA would be more accurate...
This comment doesn't parse. 1st sentence is missing 'is'. 2nd sentence 'not' throws me way off. What is this really trying to say?
xjm will probably say "yes", but is 'mixed[]' any better or more specific than 'array' for these? ;)
Confused by an array called '$new_blank_constraints' with a NotBlank() constraint.
s/is PSA/is a PSA/
a) Needs a comma before 'otherwise'.
b) s/false/FALSE/
s/version/versions/
s/links/link/
Maybe "The link to the PSA."?
s/Messages/Announcements/
s/This/The/
Start with 'The'?
Maybe worth documenting it's keyed by extension type?
s/Messages/Announcements/
I don't fully understand why we skip contrib SAs without a version. Maybe this needs a comment? Or maybe I'm misunderstanding...
I wonder what a site admin is supposed to do when they see this exception message. ;)
s/Throw/Thrown/
wants 'a' before project. But then it'd be past 80 chars. Maybe:
"Determines if the PSA versions match the installed version of a project." ?
s/if security/if the security/
s/;/,/
Do we document exceptions thrown by functions we call? That seems clumsy. I thought the whole point of exceptions is they can happen anywhere in the call stack...
This doesn't parse. Maybe:
"If an individual constraint throws an exception, continue to check the other versions." ?
s/links the/links to the/
Maybe: "A link to the upstream announcement using the message as the text." or something? It's not just returning the message, the interesting fact is that it's hard-coded to be a link tag.
s/version/versions/
I'd love this comment to define what makes a version valid to use for comparison, so I wouldn't have to read the code to figure it out.
Now that I've read the function, it seems more like a "Convert potentially BespokeVer Drupal versions into SemVer-esque versions for comparisons" function. Right? Maybe mention something about that?
p.s. Don't we already have code like this somewhere else in Update Manager or in a core utility or component class? If not, shouldn't we?
Why private instead of protected?
I thought we were trying to kill the CORE_COMPATIBILITY constant. ;)
Gets which project version? ;) How is this different from getExistingVersionConstraint()? This comment could use some clarification.
What does "the project is not available" mean? Is this "the project is not installed"? "Not enabled"? Other?
Static var since ExtensionList::getList() is too expensive? Seems like this will use a lot of RAM for something we're already storing in RAM. Perhaps worth a comment to justify this. Or maybe rip it out? Not sure...
s/Messages/Announcements/
A) s/A/An/
B) Does this want to be more specific?
Should we use 'PSA' here to avoid overflowing 80 chars?
Would maybe be better if this variable was 'announcements'...
Should this care about plural?
Looks like this
<p>is missing any visible payload.Is this right?
Is this here for things we can't parse?
LMAO! ;) Love it!!
More like "Reads a JSON file and returns the contents as a Response." or something, right?
Should it also mention the magic about replacing [CORE_VERSION] with the current version of core?
Can this explain when you get a JsonResponse vs. a regular Response? Response is only for a 404 error condition for file-not-found, right?
Not sure why we're doing this. Why not call the project name 'aaa_update_test' in the feed fixtures? Or this is here to test the case where the module name != project name? In that case, maybe spell that out more clearly in the comment?
Looks like it tests that we don't send email for malformed PSA JSON...
why capitalize Public?
Same comments on this template above apply to these copies in stable and stable9...
Comment #72
tedbow@dww thanks for the review!
Looking at the name of this service in particular I don't think we need "Update" in it at all. After talking with @xjm and other in security team, strictly speaking a PSA could be a notification about a security problem that can't be covered by an update.
So changing this to UpdateFetcherInterface. I think the PSA feed simple enough that we don't need the "Processor" "Manager" and "Fetcher" that we do for the actual update XML logic.
Other naming changes in this patch. Some related, some just giving harder look:
update-psa-notify.html.twig$this->tempStore->get('updates_psa');to$this->tempStore->get('psa_response');. HeretempStoreis already theupdatetempstore so we don't need to repeat it.update.psatoupdate.psa_fetcher. We already have theupdate.psa_notifyservice so this makes it more clear what their responsibilities are.logger.channel.updateOk going to update with the above changes because the naming changes make a 13k interdiff and splitting it should make reviewing easier.
Assigning to myself because I am continue to address the rest of #71
Comment #73
tedbowContinuing with #71
PsaFetcher\Psr\Log\LoggerInterface::error()and they don't all at()function. RemovingI am going to remove the 2 services that this patch adds.
I think if there is case for custom/contrib code to need the interface the case can be made later. It seems like it would be such a rare case to want to want to change this functionality that it would be almost as simple for custom code to just fetch the json feed themselves.
The problem in that case would be that our services would still be doing their thing so they might get duplicate messaging.
I am going to add
Drupal::hasService('update.psa_*')checks so that custom code can still just unset our services if they want to write their own functionality to handle PSAs.
c, the current docs on https://www.drupal.org/docs/8/update/automatic-updates#s-public-service-... say
It could be just a "Security Advisory". I am not super clear on the distinction since by the very fact they are published make them all Public.
I think the distinction is a PSA is something that is released before an upcoming release where as a SA if for a security vulnerability or fix that is already released.
Maybe @xjm @drumm or somebody else from the security team could confirm this.
What I found https://www.drupal.org/docs/develop/standards/object-oriented-code#naming is
it doesn't explicitly say anything about properties. I did find
\Drupal\jsonapi\Routing\Routes::$jsonApiBasePathwhere JSON and API are both acronyms.I couldn't think of other acronyms to grep for. Didn't find "xml" or "yml" used as properties.
Basically PSA are for future releases, a security release will made available very soon.
So if there is PSA for Drupal core, meaning a warning about an upcoming security release,
$insecure_versionswould contain all the versions from https://updates.drupal.org/release-history/drupal/current NOW that have the term,Release type == insecure.It does include versions will be insecure after the upcoming security release.
We can't this in JSON feed because it is already used by the contrib module and also it could be used by other custom code.
$not_blank_constraintsfixed
Have to break. I will continue on with #71 after a couple hours
Comment #74
tedbowContinuing with #71 (thanks @dww a lot good points and questions!)
Agreed it should be changed but I am trying to get clarification now as to what proper naming should be.
on https://www.drupal.org/security/psa
We have both "Security Advisory" and "Security Announcement". @drumm has said on drupal.org he is moving towards "Security Advisory" because it is more standard across projects.
From the feed it seems that a PSA is special type of "Security Advisory". Right now the feed and the command on drupal.org to create the feed handle both.
So we may want make more changes like change the newly renamed "PsaFetcher" to "SecurityAdvisoryFetcher" since it fetches both kinds.
isValidExtension()which requires a version number in the project.I think we should figure out what we want to do. for non-psa items the logic now is to only show items that have a match in
insecure_versions. I think the reason here is because these aren't for future release soinsecure_versionsshould reflect the versions that are affected and need to updated. So for non-psa items it may make sense to not show if the extension does not have version because we can't determine a match.On the other hand maybe we should default to showing non-psa items if we can't determine the existing version, just to be safe.
Another problem here is that there maybe advisories that can't be solved with an update. Rather they may instruct the admin on some server config change or drupal config change that can't be automated(does someone know a previous example of this?). In that case
insecure_versionswouldn't tell us anything. Maybe these will always be PSAs?Because 24) raises some questions about how this should work and I have some other questions about the current version matching that was brought in from the contrib I am going to pause on address #71 and switch to writing a kernel test where we can clear test cases via a dataProvider. That way I think it will be easier for everyone looking at this patch to have the same understanding of when an item in the feed is displayed and when it is not displayed. We can determine if the current functionality is correct or if it should can change(I think it is probably not correct)
Couple of questions to address
versionwhat should we do for non-psa and psa?Will these always be
is_psa === 1? If so it is easier because we don't need to worry about matchinginsecure_versions. If not I don't think we have enough information in the feed to tell use whether to checkinsecure_versions. We don't want to ignoreinsecure_versionsbecause we don't want show message that we don't have to.\Drupal\update\Psa\PsaFetcher::getContribVersions()has logic that converts Drupal module version numbers then in\Drupal\update\Psa\PsaFetcher::matchesInstalledVersion()we use\Composer\Semver\Constraint\ConstraintInterface::matches()This handles the case where the installed version is
8.x-1.0butinsecure_versioncontains1.0and the reverse case.Would this ever happen? I know why we do this conversion in other parts of the Update module but there we are dealing with greater than/less than versions to determine updates. But here we have exact versions that we are matching.
We know the version number that should be in the project and we know how the
insecure_versionarray is generated.In which cases would not be exact string matches of the versions?
If we don't need this type of match we can really simplify the logic here.
for know the test case I added will match
8.x-1.0 == 1.0just to show the current functionality.Comment #75
tedbowforgot @group on new test
Comment #77
tedbowI forgot to update the namespace after I changed this from a unit to a Kernel test.
We could make this a unit test but the mocking is pretty complicated. I think the kernel test is more readable and relies more on core real functionality.
Comment #79
tedbowThen I think we can simply the logic and remove the cases where it matches but should not.
Comment #80
tedbowChanging this to
matchesExistingVersionas the extension might not be installed.I don't think we need to worry about local caching for
$extensionshere.\Drupal\Core\Extension\ExtensionList::getList()already takes care of caching for us.Ok I removing the logic from
PsaFetcherthat parses versions to match these non-exact versions.So it will only match if the existing version is in
insecure.I chatted with @drumm and he said the version in
insecureshould be complete version strings.insecure. For items whereis_psa == 1this won't matter because we don't checkinsecurein that case.We could open a follow up to decide what we should do.
is_psa === 0and the site is using dev version of core or a contrib extension I think a solution would be to show all items where the major version of the local project(core or an extension) matches the major version of any version ininsecure.I don't think we should try to match the minor version. Here is example situation why.
insecurewould match your minor only your major.This example maybe seem far fetched but having any items in this feed seem like it will be very rare and we don't a chance to fix it if we mess up the logic before one gets added. I would rather error on this side of showing more messages to sties running dev version than having them miss critical SAs.
insecurewould not work.This could also be true contrib projects
insecureis that for cases whereis_psa === 0we know the versions that are affected and we only need to show the message to sites that have 1 of those versions. But sites running a dev version we really can't be sure about the relation between the code you are running and security vulnerability that is in one of the version ininsecureI think for this reason probably the best solution is for projects where the site is running a dev version we show all items in the feed for that project regardless of the values of
is_psaorinsecure.I have not done this yet but will do it next.
I am leaving assigned to myself because I am still working on the patch but don't mean to stop anyone else from chiming in my last comments/patches.
Comment #81
tedbowThere tons of places in this patch that uses
psaor "Public Service Announcement" where what is really being dealt with is a "Security Advisory" and some but not all of these "Public Service Announcement".So removing "PSA" or "Public Service Announcement" except in the explicit places where we dealing with PSAs.
I know this causes a huge interdiff but it just doesn't make sense to a have PsaFetcher that is fetching and processing PSA and other types of Security Advisories.
Talking with @xjm, @mlhess and @drumm I confirmed that PSA are specific kind of Security Advisory. The feed https://updates.drupal.org/psa.json may actually contain both kinds.
I left "PSA feed" in because that is what is documented as.
Comment #82
tedbowBack to @dww review in 71
\Drupal\update\SecurityAdvisories\SecurityAdvisoriesFetcher::getSecurityAdvisoriesMessageswith the change in #81 to not use "Public Service Announcements" when their will be other kinds of advisories. I would like to keep "Messages" here because we are not returning all the data about the item just the messages.isValidExtension()would return FALSE if there was not "version" set in the info. If that makes sense we could leave the way it is. But I think for$sa->isPsa() === TRUEwe should show the message regardless of whether we have a version or not since we don't check the version anyways. Adding back ingetProjectInfo()we can use this to check if the project actually existing locally and skip if not. We can then call it fromgetProjectExistingVersion()which simplifies that method. Also checking!isset($this->extensionLists[$project_type])directly here and taking it out later. If the project type is not valid we can't do anything.I have also added a new test case to
\Drupal\Tests\update\Kernel\SecurityAdvisoriesFetcherTest::providerShowAdvisories()to confirm we show a PSA even if there is no local version set. There are already test cases to show we don't show a non-psa if there is no local version and we don't show either if the project does not exist.If appears once here:
At this we actually know it is valid JSON because
json_decode()didn't return NULL. So the exception would only be thrown here if an individual item in the feed didn't pass the validation in\Drupal\update\SecurityAdvisories\SecurityAdvisory::validateAdvisoryData()I think in this case we should actually just skip the item and continue to the next item, if any. It seems very unlikely that the feed from drupal.org would have some valid and some not valid entries but if that were to happen for some reason we should process any items that we can because they may contain a critical advisory that should be displayed on their site. Removing the exception.
The 2nd time this exception thrown in the class is if
json_decode()returns a NULL meaning the json item is malformed. I added the codeMALFORMED_JSON_EXCEPTION_CODEto try to distinguish this from a\UnexpectedValueExceptionthrown when calling\Composer\Semver\VersionParser::parseConstraint()but since we no longer calling that because we are not doing version parsing then we don't need this code. Removing the code.I am going put off deciding what the admin should do with this error message until I relook at the part of the patch where the exceptions are handled. As far as the service is concerned I think it should throw an exception that has valid information. I think a caller of
getSecurityAdvisoriesMessages()should be able to tell the difference between an empty array meaning there were no items that match conditions to show on the current site and the feed not being able to be processed.Not using "PSA" anymore and using "existing" because the project may not be installed
parseConstraints()parseConstraints()@returncomment and the new name isgetProjectExistingVersion().getExistingVersionConstraint()has been removedTrying not to use "installed" because now it just matters if the project exists in the code base not just if it is installed.
\Drupal\Core\Extension\ExtensionList::getList()handles caching. removed in #80.2message()method above in 33). I am actually removingmessage()now because it is only called in 1 place and has no logic.I could be convinced no to remove it but it seems more readable.
update-advisory-notify.html.twig. Changed in other templates tooSee the <a href="{{ status_report }}">site status report page</a> for more information.. This was a recommendation from the UX that I forgot to implement. There is not actually more information on the status report page because it just has the same messages with links. Also if you happen to read the email after the items had been removed from the feed from drupal.org then the messages won't appear on the status report page at all which might make you think the problem doesn't exist which is probably not the case.is_psa === 1the version ininsecureare actually ignored. Because it a PSA is about future release. Hopefully the test cases in\Drupal\Tests\update\Kernel\SecurityAdvisoriesFetcherTest()make this more clear. We can't put test comment in a json file like we can XML ☹️A few other changes to replace psa with advisory where needed
update-advisory-notify.html.twigadvisory_notifyAdvisoryTestControllerUnasigning myself as I am not working on the patch but I will now work on making sure the summary is update to date
Comment #83
tedbowUpdating summary
Comment #84
tedbowinsecurein items withis_psa === TRUEComment #85
tedbowFound a few small problems
I think we should calling
\Drupal\Core\StringTranslation\StringTranslationTrait::formatPlural()instead of using this class directly. I was trying to figure out why this class neededStringTranslationTrait. It does right now but I think using the trait is preferred. I see many more times in core.I added an explanation here as to why just skip items that are in the incorrect format.
I don't think will ever actually but in the very slim chance it does we should display the items are in the correct format and error out if one is in an invalid format.
This actually fails if the json has
false. changing to handle values[1, '1', 0, '0', TRUE, FALSE].Right now in the test feed https://updates.drupal.org/psa-this-is-only-a-test.json
it is using
"1"but I think we should handle these other values in case for some reason, intentionally or not, the value type should changes.With this change I am creating a unit test for this class to fully test it.
Trait not used.
changing function name to
_update_advisories_requirementssince these will not just be PSAs.In this case the known exceptions that are instances of
TransferException(could be subclasses) from\GuzzleHttp\Client::get()orUnexpectedValueExceptionwhich is thrown if the json feed is malformed. We should matchUnexpectedValueExceptionexactly here and not its subclasses.Comment #86
tedbowis_psa === 0item.Here is solution I propose
If the site is using a dev release:
If the
is_psa === 1then show the item. this is how the patch works currentlyIf the
is_psa === 0thenIf the dev version only specifies a major version like 6.x-dev for branch using semantic versioning or 8.x-6.x for a branch using the legacy versions then
Show the item if it matches the major version for any of the versions in
insecureIf the dev version specifies a major and minor version like 9.1.x-dev, for contrib or 9.1.0-dev, for core. then
Show the item if it matches both the major and minor versions for any of the versions in
insecuregetMinorVersion()to\Drupal\update\ModuleVersionso we don't have string parsing in the new class.getMinorVersion()was actually in\Drupal\update\ModuleVersionin a early version of #3074993: Use "current" in update URLs instead of CORE_COMPATIBILITY to retrieve all future updates where we created the class but we removed it because it wasn't needed and the class is internal so weren't intending to make a class for use outside of corefor my previous concerns about dev version. I don't think there is perfect solution to this problem but I think matching as much as we can is good.
Comment #87
tedbowUpdate summary with dev version logic
Comment #88
dww@tedbow - Thanks for all your fantastic work in here to get this ready, and for so thoroughly and thoughtfully addressing all my review points!
I've carefully reviewed all your comments since #71, but not every single interdiff. The interdiffs I reviewed, I liked what I saw. ;)
I'm +1 to having "Advisories" as the main terminology here, and differentiating PSAs from SAs. Thanks for sorting that out!
+1 to erring on the side of showing more advisories than we might technically need to show. Way better than not showing something we wished we were showing in an edge case. Fail safe. ;)
+1 to ripping out the complications of partial matches. Your reasoning on being able to do exact matches makes perfect sense at #74.3
+1 to punting on perfection for -dev. Update Manager has always had trouble with -dev, and -dev is just problematic in so many ways. ;) We can always try to improve things later. Better to get something out that handles official releases well, than to delay that while trying to solve every possible -dev thing. #86.1 seems very reasonable for an initial release of this feature.
Re: #86.2: Hah! Guess we can un-postpone #3110223: Add ModuleVersion::getMinorVersion() method. ;) Probably just mark it fixed once this lands.
I'm going to do a final review of the full patch next, but this sounds like it's either RTBC or darn close.
Thanks again!
-Derek
Comment #89
dwwAssigned by accident, sorry.
Mostly everything looks great. The test coverage is extremely thorough. Basically everything that needs to be fixed was fixed.
I know we're almost out of time to get this into 9.1.x, but it's going to be painful to change the tempstore keys later, and some of the test coverage isn't what it says it is. That's why I'm setting NW. Everything else is basically trivial docs stuff or code polishing that could be done in a follow-up. But if we're rerolling anyway, maybe worth fixing now.
Thanks again!
-Derek
s/an a/
Wonder if securityAdvisoryFetcher would be better for this var.
s/psa_fetcher/sa_fetcher/
(or security_advisories_fetcher if the previous comment happens).
Don't love the double-plural, but probably not worth fixing. ;)
Is this the "PSA feed" you decided to leave as-is due to existing documentation or something?
s/PsaFetcher/SecurityAdvisoriesFetcher/
Do we want to stop keying all these tempstore things with "psa"?
$this->t()
s/PSA/security advisory/ ?
Wants a comma between "minor version" and "matching on".
a) s/then insecure/then the insecure/
b) comma after 1st 'minor version'
"If the existing version is not a dev version, then it..."
Should have caught this before, but presumably this is more the URL for the advisory, not a "link" (e.g. not
<a href="...">blah</a>Undoubtably that's the key in the feed itself, which is why it's like this. So probably nothing to fix...A) Thanks, this helps!
B) "the 'aaa_update_test' module"?
C) s/json/JSON/
Why static:assertCount() here? We don't do that anywhere else in core.
Super minor, but it was a bit jarring to see this among the contrib. Seems easier to make sense of this list if we keep all of contrib together, then all of core.
The name says "reversed", but these two cases are identical.
These 2 also have different names for the same case.
Name says "semver" but these are BespokeVer strings.
I think to be safe we should use example.com for this.
Doesn't this want \Drupal:: not just Drupal:: ?
Again, s/$psa/$fetcher/ (or something).
"psa" is now a weird local variable name for this.
s/SecurityAdvisoriesFetcher/whatever the right class is/ ;)
Comment #90
dwwThis fixes nearly all of #89:
$securityAdvisoriesFetcherThanks again!
-Derek
Comment #91
tedbow@dww thanks for the review and patch!
re #89
getSecurityAdvisoryMessages()Hopefully we can update that documentation later.
advisories.and the tempstore key to beadvisories_responselinkbut I changed the class to usedURLand changed the method togetUrl()because the 1 place we use it in non-test code is':url' => $sa->getUrl(),It looks to be the one you didn't fix is 18.
I changed the 2nd test case there and added a new 1 after that
So making sure we don't do a partial match on semver if the site is NOT using a dev version.
1 Additional change and 1 suggestion(not implemented)
Following the example of
\Drupal\update\ModuleVersionI am marking the class as @internal and making itfinal. I am also changing the constructor to private since we have a public factory methodcreateFromArray().I think the same argument I used for this pattern in
ModuleVersionapplies to this class. You can see my argument here #3074993-90: Use "current" in update URLs instead of CORE_COMPATIBILITY to retrieve all future updates and in the next comment @xjm agree and giving more reasons.Here is @xjm's reasoning which I think applies directly in this case also
For this reason I would be in favor of making the other 2 classes in the
Drupal\update\SecurityAdvisoriesnamespace also final and @internal. We could always removefinallater if a good case was made to do so. We can never make themfinalthough if we don't release them that way.I am not willing to hold this issue up for this. I imagine it is not a popular opinion but just wanted to put it out there in case it makes sense to others. I guess technically we could do it in follow-up if it made it in before the code was in a release.
Comment #92
dwwfinalis so final. ;) What's the point of making this a service if folks can't decorate it if they need to?That's pretty harsh. 😉 This is a lot of code, much of it would be relevant to someone else trying to use this functionality but that needed some additional changes / tweaks. Telling them: "sorry, start from scratch if you don't like what we're doing" isn't very Drupal-esque.
How about we mark them
@internalas a hint, but not actuallyfinal? Is that weird?I also think we could change our minds on this during alpha (if not beta), so I'd rather not hold this up for commit. If you want, we can open a follow-up to continue this discussion.
I've carefully reviewed the latest interdiff, and very closely reviewed the full patch twice. Try as I might, I'm out of things to complain about. 😉 Bot's happy. Lots of great new test coverage. No CS violations. Summary is accurate with screenshots, API changes is correct, etc. It was missing a release note, but I added one.
This is important functionality to help improve site security. Let's get this in. RTBC 🎉
Thanks again, @tedbow, @beautifulmind, @heddn, @drumm, @xjm and everyone else who worked on getting this to this point!
Cheers,
-Derek
p.s. Adding #3110223: Add ModuleVersion::getMinorVersion() method as related. It's possible there's some useful test coverage in that patch we can still harvest even after this lands. Of course, not a blocker or urgent. ;)
Comment #93
tedbow@dww thanks for the review again!
@xjm asked me to put the "Needs release manager review"
Comment #95
pasqualleComment #96
xjm@Pasqualle, only committers should move minor-only issues back to 9.1.x after the release of alpha1 (and generally only after commit). Thanks!
Reference: https://www.drupal.org/about/core/policies/core-change-policies/allowed-...
Comment #97
xjmFor what it's worth, we probably will backport this issue, but following the issue deadline this decision is made only once the patch has successfully been committed to 9.2.x. So setting the branch accordingly.
Please do not change the branch elsewhere for minor-only issues; if they don't get backported during the alpha at committer discretion, what you're doing is mixing issues that were specifically targeted against a minor branch in with the patch queue that 9.1.x will shortly become.
Comment #98
xjmOne thing that occurred to me since we trimmed down the email message to this for readability:
Among other things, we deliberately took out this bit:
Since then, I've been wondering if this violates some user rights best practice of explaining to someone how they can unsubscribe for an email notification. Going to ping the sec team to see if they have thoughts on whether we need to include such a sentence here. (If we do have to add it back, we could just add back the last sentence rather than both of them.)
Comment #100
xjmPer @webchick, we do indeed need a link telling the email recipient how to change the notifications. @webchick referenced the CAN-SPAM, act, and I believe there are other legal precedents as well.
I think just the second sentence will do:
Comment #101
tedbowUpdated email
Comment #102
dwwInterdiff looks great.
All 3 templates updated, and test coverage expanded to match. Tests are all passing.
No CS violations.
Back to RTBC.
Thanks!
-Derek
Comment #103
xjmReviewed down to the start of the tests. Mostly nitpicks but one critical question WRT dev versions in point 12.
Didn't we discuss using an existing configuration setting for this check frequency?
I was going to say that this is a BC break, but we did make the constructor private.
"Constructs a"
"Sends notifications"
Missing space before the colon.
Since this can end up as a user-facing string, let's use a complete sentence with articles:
Why a tempstore?
Hm, code comment here would be good as it's not immediately obvious why we skip it under these conditions.
Noe to self: check for where this bool flag is set.
"or FALSE otherwise."
Note to self: Check for test coverage of all three cases here.
Wait. With a dev version, we have no indication of how recent it is. It could be from yesterday or five years ago.
Can we be more specific than array? And where is the array structure canonically defined?
Also: "or otherwise".
Missing space before the colon again. We should check for or file a coder rule followup for this.
We have a content standard that "Drupal.org" should be capitalized.
Why "does"?
Spaces before the colons.
Should we omit the word "Drupal" here? Going back to the content standard of avoiding using the word "Drupal" in user-facing content since it can be branded as something else. Edit: Just checked and the existing email notifications about insecure sites do say "Drupal" already. Dunno if we should file a followup about that or let it be. Maybe just let it be since it's been this way forever.
Comment #104
xjmAlso: comma splice. Should be two sentences.
Comment #105
effulgentsia commentedRight. So the patch errs on the side of displaying the SA. There's a chance you have a dev snapshot that already includes the fix, but we can't determine that. However, it shouldn't be all that likely that you have a dev snapshot that includes the fix. The most common reason to be on a dev snapshot is that you need to be ahead of the last tag that was made on that branch. But it that was your situation prior to the SA, then once there's an SA, there's either a new tag on that branch, or that branch is no longer supported and you need to update to the branch that has the security fix and there's now a newly released tag on that one. Ok, but what if a day or a week after that SA, a non-security bug fix gets committed on that branch, and your site needs it? This is the situation in which you'd now be on a dev snapshot and seeing the old SA again, even though you already have the fix. While not ideal, is it acceptable, considering that if we don't show the SA for dev snapshots, then the more common case of someone being on an old dev snapshot would not see the SA?
Comment #106
ayushmishra206 commentedI have made the following changes in this patch:
103.3
103.4
103.5
103.6
103.10
103.13.2
103.14
103.15
103.16
103.17
and #104
Remaining changes are:
103.1
103.2
103.7
103.8
103.9
103.11
103.12
103.13.1
Comment #107
tedbow@xjm thanks for the review and @ayushmishra206 thanks for addressing many of the items!
re #103
\Drupal\update\SecurityAdvisories\SecurityAdvisory::__construct()also privatecatchin\Drupal\update\SecurityAdvisories\EmailNotify::send(). I think my intention was to not to have an exception that we were not expecting stop cron. Now looking at\Drupal\Core\Cron::invokeCronHandlers()exceptions are already caught and handled. So removing this$throw_unexpected_exceptions\Drupal\Tests\update\Kernel\SecurityAdvisoriesFetcherTest()mixed[]. This is from the info.yml files but augmented in\Drupal\Core\Extension\ExtensionList::getList()or where this function overridden and also passed throughhook_info_alter()I don't believe we have a canonical place where this structure is documented. Update the @return comment
The example in the proposed change in #2928856: Adopt the PSR-12 standard for PHP return types suggest not to have a space before colon. grepping in /core for "function.*\):" with no space finds over 100 results and "function .*\) :" with the space only finds 27.
Not changing any more for now
Comment #108
xjmMakes sense.
In that case, can we be consistent the other way, and not have the space before the colon anywhere? This sort of violates core's standard of having a space on either side of an operator, but we should at least be consistent within the patch.
#2928856: Adopt the PSR-12 standard for PHP return types doesn't actually specify whether there should be a space before the colon or not. It only says there does need to be a space after it.
Comment #109
xjmThe second sentence should be:
It should have a period at the end.
Can we add some inline comments above this section explaining what @effulgentsia explained?
URLs should use
:placeholders, not@placeholders, so that they receive the proper sanitization.Nullable typehint?
Return typehint?
Comment #110
xjmSkimmed the test coverage, which is beautiful, and reviewed down to the top of the
update.modulechanges. A few nitpicks again.Return typehint?
"Data provider" should be two words.
Return typehint?
Nit: Advisories does not need to be capitalized here.
"Data provider" should be two words.
Return typehint?
Beautiful. I love it when our past test coverage lets us add new test coverage so cleanly.
Void return typehint?
Return typehint?
Comment #111
xjmWait. We're raising requirements errors from exceptions in the fetcher on the status report? Can the site owner do anything about it if there's exceptions when we try to get advisories?
Comment #112
xjmWhat's with the backslash in the URL?
Comment #113
tedbowre #108
Yep here is a fix for that
I think #2928856: Adopt the PSR-12 standard for PHP return types does
(emphasis added)
I double checked with psr-12 directly and it still has that wording https://www.php-fig.org/psr/psr-12/
the examples on that page also use it consistently
public function sampleFunction(int $a, int $b = null): array#109
@urlin the update.module file are actually existing code. They are only in the patch becauseupdate_mail()now has a if/else and the lines are now indented. Maybe we should create a follow-up to address along with others in the update module or in all of core? I found some othersFixed the instances in twig files.
second ordered list in #109
\Drupal\Core\StringTranslation\TranslatableMarkup|stringre #109
☺️
re #111
see #58.13
the site can't do anything if the problem is on the drupal.org side but they could do something if the problem with the request is something on their network side. It's been so long since I have dealt with network issues that I can't imagine what that might be.
But if there is some problem on their side and we never show them a message then when a PSA comes along they won't see it and we won't have raised any error they could see anywhere. To much of edge case?
Leaving for now.
re #112
🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉
Congratulations!!!
This backslash was left in the patch as part of the weekly Most Thorough Reviewer Award™️
You have won!!! 🍾
Expect your award check in 6 weeks to 8 decades!
🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉
😉 Also fixed!
Comment #114
xjmInterdiff looks good. Two small fixes:
The period is still missing at the end of the sentence.
I think this should be "err on the side of". "Error on the side of" is a common misuse of the idiom.
@tedbow and I also discussed that for #111 we can log the error instead of showing it on the status report. That way the site owner can still find it, but it's not a super-serious, potentially inactionable error on the status report.
Comment #115
xjmOh, missed this before since I don't review templates all that often, but there should be a period at the end of the sentence here too.
Comment #116
xjmUpdating the release note snippet with examples of the kinds of PSAs that will end up in the feed.
Comment #117
xjmComment #118
tedbowre #114
#115
Fixed but changed to "An array of message strings." across all 3 twig files since this had been updated because of prior comment by @xjm but I missed it in the other 2 twig files.
Comment #119
xjmSo, this is one of the exceptions that could end up logged in the logger. I wonder if we should add more context to it? Otherwise I think the constraints' errors will be quite generic. It could be something like:
Comment #120
tedbowfixing #119
Comment #121
dwwVerified that #120 addresses all the feedback since #102. Here's a fix for one remaining extremely trivial minor grammar bug. 😉 Self-RTBC'ing, since this is so tiny.
Thanks!
-Derek
Comment #122
tedbowJust a reroll
Comment #124
xjmKnown random fail. Rerunning.
Comment #125
xjmPartial review:
We need to either add "PSAs" to the cspell dictionary, or (preferred) write it out as "public service announcements":
I checked and update module itself checks daily by default, and is only configurable to the day, not the second:
The update settings form allows configuring this to be a day or a week, and someone could presumably set it to another value with contrib or custom code.
On the one hand, 12 h is reasonable as a default -- twice as frequent as the default for updates. I could honestly see checking for security updates at least twice a day but checking for other updates only once a week.
And in some ways, having fewer configuration options in the UI is nice.
However, OTOH, I wonder if this might confuse people because they can control the update check frequency through the UI, but not the security announcement check frequency.
Our three options are:
So I guess the first option might still be best, but I do get stuck on it every time I open this patch.
What if
$version_parts_countis 4 or more due to a change in version formatting?Answer: An exception is already thrown in HEAD in that case, so such a version can't work anyway.
Missing a docblock.
Let's get rid of this string entirely. These messages are so rare, like once every two years. There are definitely zombie sites for which there would be two messages, but all of them have also probably been pwned already by the time there's a second message for the list. And even then we don't need to tell them how many bullets are in the list, because they can count them. The combination of the left column and the PSA link title(s) are sufficient to explain this.
This is a bit of a run-on. How about:
Also, let's call them "items" or something. They're not really "messages".
Typo: thrown.
So, I'm still fussing a bit about this exception re-throwing, and about unexpected exceptions from connectivity or timeout issues with requests causing sites to whitescreen / cron to fail / etc.
I checked for all the places the method is called to make sure we're catching and logging exceptions. It looks like the third case, in
update.module, is re-throwing exceptions that aren't one of two types, while the other two places are simply catching and logging all exceptions. Why does this last one have different behavior?This is only ever
UnexpectedValueException, because that's what we've defined in the new method and its validation.This returns NULL if the feed can't be decoded, so no exception there either.
However, this might throw other errors, and that's what I'm worried about in terms of breaking site operation.
I cannot, for the life of me, find where this
get()method is defined. There is only one reference in a docblock onGuzzleHttp\Clientto:...but that doesn't actually seem to exist on
ResponseInterfaceand I can't find where it's implemented.Then in the Guzzle docs there's this line:
...but it doesn't say where said magic methods are defined or how they work.
How does it work?
All that said, looking at the List of Guzzle exceptions, it seems theoretically there are a lot of exceptions that could come out of network issues, timeout issues, etc. that wouldn't be caught here and would break the mail service or cron or etc. Maybe we should catch and log all exceptions in all three cases where the method is called? Or is there a reason that the
update.modulecase is different?Comment #126
tedbow@xjm thanks for the review!
interval_hours` which is more easily understandable and more similar to the existinginterval_daysfor updates.throw new \UnexpectedValueException("Unexpected version number in: $original_version");So we have to update the code if we want to support different formats
It sounds like from your comment you were describing this as the status report text. I noticed the text in the status report is very similar so removing it from there
$messagesto$itemsI left
':message' => $sa->getTitle(),as 'message' I think changing to 'title' would be confusing because
titleis an attribute for<a>elements.Comment #127
tedbowwhoops here is the patch. can't wait till all of the issues I am involved are merge requests
Comment #128
tedbowYes this weird what actually gets executed here is
\GuzzleHttp\Client::__call()which passes$methodon torequestAsync()orrequest()of the same class.I had said I would look to see how the Update module currently handles display errors to the user when it has problems attempting to get the update data via drupal.org update XML feeds.
I had said that I thought I didn't report an error to the user until it had a certain number of fail attempts. This was from briefly looking at the code around the setting
max_attemptssuch as
It turns out I read this wrong. 😅. Actually it reports an error to the user every attempt saying "Failed to fetch available update data:" with a link to this handbook page. This message was just added in #1538118: Update status does not verify the identity or authenticity of the release history URL. It also shows this message if didn't just attempt to request the XML but the last time was a failure.
What the
max_attemptsis actually for is to limit the number of times it will attempt to get XML on failure before the update data is cleared.I don't think we actually need something like
max_attemptsfor this new code. The big difference is each project has its own XML so if you have 50 contrib modules that would be 50 attempts each time updates are checked. But with the PSA feed it is only 1 attempt each time it is tried for every Drupal site regardless of the number of extensions installed.So I think we should follow the same pattern here and report the fetch error to the user on the status report. For this problem with update XML the status report has a minimal message but then links to
admin/reports/updates/updatesince we don't have page like that for PSAs we should probably link the handbook page directly on the status report page.\Drupal\update\SecurityAdvisories\EmailNotify::send()because this is done on cron which handles catching and logging exceptions_update_advisories_requirements()to only catchGuzzleExceptionand display a similar message and link to the same handbook page that is already displayed if there is problem fetching XML.Right now i am reusing the newly added
update-fetch-error-message.html.twigandtemplate_preprocess_update_fetch_error_message()to display the message.This templates description is
which I think this case fits.
This allows removing the awkward
\Drupal\update\SecurityAdvisories\SecurityAdvisoriesFetcher::getErrorMessageFromException()Comment #130
tedbowUpdateReportTestfail because it callstemplate_preprocess_update_fetch_error_message()directly and didn't set the new value. It has a default value so any non test callers should use the render pipeline which will set the default value.Comment #131
tedbow🤷🏼♂️
Comment #132
xjmCan we add an inline comment here pointing to the spot where cron handles exceptions?
Nit: Drupal.org should be capitalized and there should be a period at the end of the sentence.
I can't remember if the logger logs strings or plaintext. Either way, we should probably provide an action in the logger message (with a link to the handbook page we discussed, I think).
When would this be the case? Are we talking submodules, like Views and Views UI of old? I thought we got rid of submodules during D8 development. Or something else?
"Test that the site status report displays an error."
Weren't we going to link a handbook page here for debugging problems with the feed? It might need to be created still; the existing docs are more developer-focused and we should create a page that has troubleshooting info for site builders.
Since my original review, I learned a lot more about Guzzle's exceptions and how we respond to them in core. Since then #3188920: Make Guzzle exception handling forward-compatible with Guzzle 7 has also been committed, which updates all instances of "try a network request and catch and handle a failure" in core to use
TransferException. There are no places that useGuzzleExceptiongenerally in core, and there is also a followup for D10 #3189301: Use \Psr\Http\Client\ClientExceptionInterface instead of \GuzzleHttp\Exception\TransferException to use a standard PSR interface for the exceptions.So I was wrong and I think we can just respond to
TransferExceptionas well.That aforementioned handbook page should be linked here too.
Comment #133
xjmOne other thing. The message says "failed to retrieve security advisory data," but as previously discussed, they're mostly PSAs and might sometimes be SAs. So I think we should say "security announcement data" to cover both cases.
Comment #134
tedbowupdate_cronwhere this is called. I also add a@throwstag here to document the exception could be thrown.mymodulevsmy_moduleor the machine of the module just being initials and the project name being words separate by underscores\Drupal\Core\Utility\ProjectInfo::processInfoList()which is called by theUpdateManagerbecause the update XML always has this same problem. Drupal.org packaging addprojectto the info.yml file.template_preprocess_update_fetch_error_message()which will be displayed here. This is the page that is linked is https://www.drupal.org/node/3170647. I think the same problems would apply but we would probably need to update explain the new use caseComment #135
phenaproximaThese are all relatively minor points. For my own reference, I got part of the way through the patch; stopped at the first kernel test.
Do we need an update path, to set the default values for these settings?
Two things, both nitpicks: 1) EmailNotify seems like an odd name for a service class. Would
EmailNotifiermake more sense? 2) Should we mark this class internal?Might just be a brain glitch, but I'm not seeing where we're using this...?
We should use
$this->setStringTranslation()here, so that we're consuming StringTranslationTrait's API.Do we need this? The method does not actually throw an exception; it just doesn't catch the one that may come from the security advisory fetcher.
Again, should this be internal? It feels like a good candidate for that.
According to the changes in update.services.yml, this is a regular expirable key-value store, NOT one of the tempstore services. I know they use similar mechanisms under the hood, but they're still treated as distinct concepts in Drupal, so maybe we should rename this property to $keyValueStore or something else?
I must be blind, but I don't see where we're using this either...
Maybe we could reword this for clarity? How about "Thrown if an error occurs while retrieving security advisories."?
Why not at least log the exception message as debugging info, or even a warning? If invalid items
really are highly unlikely, then it seems worthwhile to capture some information if we do see one.
Why not have $this->getProjectExistingVersion() return an instance of ModuleVersion directly?
For whatever it's worth, I think this is the right choice. 👍
Not in scope, but I wish that ModuleVersion didn't use the vague word "extra" to describe stability-related parts of the version string. That really should be getStability(). But hey, since ModuleVersion is final, maybe we can change it later in a follow-up :)
Is this the only reason why createFromVersionString() will ever throw \UnexpectedValueException? If not, then we might need to narrow this catch.
IMHO this should be elseif.
A minor thing, but why not return NULL here (and change the return type hint to
?array) if the project lookup fails? That'd be a clearer signal to calling code that the project is absent.Same question. Why not return ?string, so that the result is clearer to calling code?
This method doesn't actually throw anything, so do we need this
@throwsin the doc comment?Nit: I think we can use
selfas the return type hint if we want to.Is there any use in validating the contents of the array (i.e., that it's a bunch of non-empty strings)?
Since this is going to be in an email, surely we want this to be a full absolute URL? As far as I know, the
path()function doesn't do that.Can you add a comma after the word "match"? This is a bit hard to parse otherwise.
If these are links, maybe we should use $assert->linkExists() instead?
Why don't the Node and Views PSAs show up here? If that's intentional, this could probably use a comment.
Same question; why don't the AAA and BBB advisories appear?
Is there any reason for us not to use CronRunTrait in this test?
Not in scope, but I wish there was a helper method for this in AssertMailTrait.
Comment #136
tedbowupdate_update_9101(). Is that the numbering?hook_update_N()just documents the numbering for contrib as far as I could tellalso added an update test
EmailNotifierNot changing to internal. Going to leave it to my next patch to address what should internal or an API
$keyValueExpirablereturn in_array($existing_version, $insecure_versions, TRUE);ExtensionVersionsince it is not always a module$insecure_versions. For instances if we find "🐶" as a version number we should ignore it and evaluate the other versions.Stopping at 21) for now because @phenaproxima said he has to time to review the progress
Comment #137
tedbowContinuing with #135
I am checking with @drumm whether the PSA feed will always have
project:"drupal"for core items or if will sometimes have name of an individual extensions inside core. The contrib module has logic for finding the version of experimental modules in core which if the feed always usesproject:"drupal", we don't need.UPDATE: @drumm confirm for core advisories it will be
project:"drupal"CronRunTrait. FixedAlso
I had discussions with @xjm and @effulgentsia about what should be
internaland what should befinal\Drupal\update\SecurityAdvisories\SecurityAdvisoriesFetcherfinal but not internal.It is not internal because we want people to call it to get the messages. In this class we have some logic in
matchesExistingVersion()that we do not want encourage others to duplicate by having to process the feed themselves. This is code has logic about which versions should be considered matching and therefore which highly critical advisories should be shown for a site. If this is class isinternalthen other code should not call it and if they want advisories for their site they have to rewrite this logic. Advisories in this feed will hopefully be very rare. If there is not a real item for a year once there is a real item it is too late if the developer messed something up they may have missed the advisory and the site could already be hacked.For the same reason we should make this class
final. If we build this as a class that developers have easy access to extend core's behavior about what advisories to show on their site there is no real way for a developer to test if they have gotten this right with real data from drupal.org. Again if they extend this class but mess something up they may not know until they mess a high critical advisory.If developers want to use the JSON feed for something else, like a list of advisories for projects not install on the current site(say for aegir) they can easily write code to consume this JSON feed directly for that purpose.
@effulgentsia wondered if ever make classes not
@internalbutfinal. We found\Drupal\Core\Config\ConfigEventsamong others\Drupal\update\SecurityAdvisories\EmailNotifierI have made this both@internalandfinalI added a class comment that explains that
a)
hook_mail_alter()can be used to alter the emails sent from the classb) To send other emails or notifications for advisories the update.sa_fetcher service should be called directly.
Comment #138
tedbowSelf review
Changing this to
protectedsince all the rest are. Functionality it is the same but if we later made this class not final the diff would be smaller.This should be
EmailNotifierAdding "expireable" to the description here.
Added "that are applicable for the current site" here to make sure it is clear we don't just return links for all items in the feed.
Changing this to
$linksnow that we are return link objects.Adding a comment about as to why we always display PSAs regardless of whether they match the current version on the site
Added a comment that we set this so new fields in the JSON feed will not cause a validation error.
Also added a unknown field to
\Drupal\Tests\update\Unit\SecurityAdvisoryTest::getValidData()to prove this.Comment #139
phenaproximaRead the last two interdiffs and I think they look good! No complaints here.
Comment #140
xjmIt's failing CS checks though.
Comment #141
phenaproximaReviewing the changes requested in #132:
Comment #142
tedbowre #141 check on #132
@phenaproxima thanks for double checking this.
update_cron()see my comment in #134.1
template_preprocess_update_fetch_error_message()see my comment in #134.5Also fixed the spelling error that caused the fail in #138
Comment #143
phenaproximaI don't think I found anything really wrong here. I still want to give it a manual test before RTBC, and get these questions/comments addressed. But feel free to push back on any of them; they're mostly in nitpick territory.
Does this need a
@var string?Nit: This phrasing is a bit awkward. How about "Thrown if an exception occurs while fetching the security advisories"? Or, we could just copy the same comment from getSecurityAdvisoryLinks().
Nit: Maybe $links would be a more accurate name for this variable?
Is it safe for us to directly serialize a set of Link objects like this? Since they can carry a service reference (the link generator), yet don't have DependencySerializationTrait, it might be wise for us to convert the links to strings first before hashing them.
Nit: "1 urgent security announcement" sounds machiney. Why not go with a more natural "An urgent security announcement" instead?
Not a big deal at all, but it's unfortunate that we need to do multiple separate load operations as we loop over the email addresses. Maybe this could be refactored to load them all in one fell swoop and key the resulting user entities by their email address.
Why does this class need DependencySerializationTrait?
Nit: Maybe this should default to an empty array.
Nit: Is the word "upstream" really needed here?
I think is_array() might be a better check here, since "not NULL" still means it could be scalar.
Nit: IMHO it would be cool if $sa had a toLink() method which would generate the link, instead of relying on calling code to do it.
This could use a comment. Why wouldn't the service exist?
Don't we need to put the arguments all on one line?
Comment #144
phenaproximaIn manual testing, I noticed something:
"Critical security announcements" is maybe not the most friendly or useful wording, considering that we display these as errors. Maybe we should expand that a bit (two sentences tops) to tell people what these links are, and what they should do to make the errors go away, since they'll definitely want to do that.
Another thing I noticed is that, when I set it to use the invalid JSON endpoint, no message is ever displayed to me unless I think to look at /admin/reports/dblog. If I don't look there, I wouldn't notice that anything is wrong at all. The hook_requirements() code is catching TransferException, but no exception is thrown if the JSON cannot be parsed...so it just gets logged quietly to watchdog. Would it be useful for us to show something in the status report if the feed could be fetched, but not parsed?
The thing thing is more of a wish, but when I change the advisories.endpoint configuration setting, I need to manually clear the response cached in the key-value store. It'd be amazing if that would automatically be cleared whenever the setting is changed. To be clear, it's not very likely that anyone will ever change the setting...but the fact that it's possible means that we should probably account for it. Or just hard-code it, which I think would be fine (as long as there are workarounds in place for testing).
Comment #145
tedbow@phenaproxima thanks again for the reviews.
In this patch I am just addressing your last point now in #144 about the changing "advisories.endpoint configuration setting" and clearing the cached response
agree if that if it is config setting we have to account for people changing it. The only reason I think it is a config setting is for testing. It came from the contrib module.
Also allowing this to be changed implies that we support other feed sources besides drupal.org. If we do that then have to careful if we change the format or the processing in core because we have to consider that other sources for the json feed will not have updated to new format. I don't see a reason to support this.
For testing I figured out that if we decorate the
http_clientservice we can just switch out URI when$uri === 'https://updates.drupal.org/psa.json'to our test endpoints.Comment #146
tedbowback to #144
constdeclarations in core don't use@var. It least all the ones I looked atActually this is bit tricker. Calling
$link->toString()actually causes a problem not here but later in$this->mailManager->mail('update', 'advisory_notify', $notify_email, $params['langcode'], $params);because at that point the links are serialized.So what happens is calling
$link->toString()then calls\Drupal\Core\Link::getLinkGenerator()which actually sets the link generator service. So they do not have the service reference to the service until you calltoString()🤯I got around this by using
This doesn't set any services.
So I can still get unique string for a set of links. Also since there should never be that many links I don't think we even need to hash this string.
We can actually load the all the users at once using
We can send multiple emails and will return back all the users that match. Ultimitely this is handled in
\Drupal\Core\Entity\EntityStorageBase::buildPropertyQuery()which is using anINclause.We don't need to worry about the key to the array because the we can just get the email from the loaded user. I updated
testPsaMail()to test 2 emails to make this works(should have had this to test the loop anyways).Also
we don't need
default_langcodebecausegetPreferredLangcode()has an optional$fallback_to_default = TRUEwhich will return back the default for us. Also usinggetPreferredLangcode()is safer anyways because it handles the case where users prefered language is not an available language on the site(I guess if it was when it was selected)Now the class does not need the
language_managerserviceSecurityAdvisory. Right now it only knows about the expected keys and validation. It has no opinion on how it might be output as far is it going to output as a link or markup. Also maybe it won't be output at all, maybe it would be just read to determine if there are any messages and then the user redirected to the advisionary URL. I would rather not be connected to whatSecurityAdvisoryanymore that it has to be. For instance if drupal 10 usesNewImprovedLinkthen as it is nowSecurityAdvisorywould not need to be updatedI don't particularly want to suggest reasons in comment. The fact is core allows altering services so they could not be there for any number of reasons and if we don't check we will get an exception
But fixed because I haven't seen this done in core.
Not addressing #144 for now.
Comment #147
tedbowAddressing #144 now
There is not a universal way to remove this messages. Some can be removed by updating to a new version of a projects. Others are for upcoming releases so they cannot be removed until the new version is released and installed. Others are for changes that need to be made that don't involve project update but maybe a server change, for those the message will stay until it is removed from the feed.
Having any items in the feed will be super rare and they are for highly critical issue so I think it is ok if admins can't easily remove them. Also they will be in the feed for a short period of time(less than a week?). In addition most users will not have permission to see the items.
I also talked to @phenaproxima about deleting stored feed response when the interval config setting is changed. I have added this but only if the interval is decreased. If the interval is increased then it should already handle waiting until the new interval is reached.
Comment #148
phenaproximaNeed a space after the colon.
🐕
I agree that less is better, but IMHO that just means we need to make whatever text we do have as useful and concise as possible. I don't find the current text very useful, to be honest, but if this was signed off on by the UX team, I won't stand in the way.
Fair. Can we make sure this is documented online somewhere? Because I feel like it is a question that will definitely be asked.
👍 I think that's a great idea.
Suggestion: it seems appropriate, in this case, to have SecurityAdvisoriesFetcher implement EventSubscriberInterface and react to config changes. That keeps all of the logic related to fetching the feed and managing the stored response in the same class, which means we won't need to share underlying details, like the name of the storage key, across multiple classes. SecurityAdvisoriesFetcher has a very clear responsibility, and I think this falls squarely within its boundaries. What do you think?
If you do this, I think you might also need to do
$this->container = $this->container->get('kernel')->getContainer().Comment #149
tedbowre #148
fixed
I will make a hanbook page. not sure if we should link to the page in
update_page_top()or just in the status page.maybe after the PSA links. because I don’t think we want “click here to find out more about these messages” above the PSA links. then they might think the handbook page is enough to read rather than clicking on the actual advisory links
Thoughts
to number points
ok. I think I am against moving into SecurityAdvisoriesFetcher . I think you are right it would be nice to have it all in 1 place. If this wasn’t core and there wasn't an existing pattern I would agree
but I think that is outweighed by the benefit of following an existing pattern. If I look at the classes that implement EventSubscriberInterface in core almost all are
*Subscriber, and there are couple otherConfigSubscriberclasses. They seem to mostly to be in `[module]\EventSubscriber` namespaces.So I think if I were to look at the Update module and wanted to know if it had event listeners I would look in `update/src/EventSubscriber`. even the ones that don’t follow the naming pattern are still stand alone classes that just react to events.
I think I should a @see though to the other class
if it starting scratch I think your pattern is better. but I think in this case following the pattern might make it easier for future developers
I will not add a
@seein\Drupal\update\EventSubscriber\ConfigSubscriber::onConfigSavebecause that already has$this->keyValueExpirable->delete(SecurityAdvisoriesFetcher::ADVISORIES_RESPONSE_EXPIRABLE_KEY);which directly point to that class clearer that a
@seebut will put a@seein the other class point to this one.Still @todo add the handbook page and link to it
Comment #150
tedbowThe change would not be that hard. @xjm was going to try to get other feedback on this
Comment #151
xjmNit: time-sensitive.
Also, upon reflection, I think we should drop the words "published by the Drupal Security Team", and change the second sentence to end with "...for a fixed period of time." (We avoid printing the word "Drupal" at runtime so that distros can be rebranded etc.)
Finally, this makes me wonder if we should make the message only a warning and not an error if there's no release associated with it.
🤔 I don't think it makes sense to print a separate error message for this. Let's append it to the string for the existing message, conditionally based on help being installed, or make it an info message (does the messenger service even support those?) rather than an error.
Comment #152
tedbow@xjm thanks the review(I know more is coming 😉)
The messenger service supports "status" messages but the problem is that it groups them all together and groups the error messages together. So there would be guarantee that if we used a "status" message it would not be sandwiched between 2 unrelated status messages if there are other messages on the page. It is not unlikely there will be other messages on the page since these are admin pages.
Here is an example of doing a status message before the advisories messages and 2 after
Comment #153
xjmI'd maybe wrap the "What are critical security announcements?" in unlinked parens rather than that straggling period.
What do you think of the suggestion to add logic to make it a warning only if it's a PSA that we can't tell if they've addressed or not?
Comment #154
phenaproximaOn the help page that we link to from "What are critical security announcements?", maybe we should explicitly mention that the messages are only made visible to people with the appropriate permissions. Otherwise, admins might worry that anonymous users will see them too.
Comment #156
tedbowJust commenting the issue is update after all the Merge Request commits and comments
Comment #157
tedbowHad further discussion with @xjm and we decided to move this into the system module(see #150)
Of course it was a little move complicated than I thought 🤷🏼♂️
I think all the test should pass but somethings that still need to happen. I will comment on the merge request
Comment #158
tedbowThe test are now failing because of #2571475: Outbound HTTP requests fail with KernelTestBase
Because we now have an outbound request in
system_requirementsI updated the patch there to try to get it moving
Comment #159
catchCan we double check what happens if the request to Drupal.org times out? Thinking about what will happen if nothing is stored and someone hits the status report page - we would really not want to break that page because d.o is down.
Comment #160
mxr576#159++
I hope you do not mind that I also shared my 2 cents. There is already a scary amount conversation on this issue, flooded with patch- and PR reviews, this demonstrates the power of the community :)
Good job!
Comment #161
tedbowadded #3196368: [policy, no patch] Determine to which module the new security advisory functionality should be added at the suggestion of @effulgentsia
Comment #162
tedbowre #160
@mxr576
Thanks for the detailed review!!!
There have been a lot of discussion the but the new Merge Request comments make it so much easier to have multiple people discuss a particular section of the code!
Comment #163
tedbowre #159 @catch thanks for bring this up!
I added a comment on the Merge request so we could have a thread for this https://git.drupalcode.org/project/drupal/-/merge_requests/284#note_10995
Comment #164
tedbowSorry for all the noise here with the commits. I used the "Rebase" button on gitlab because #2571475: Outbound HTTP requests fail with KernelTestBase was committed and that is need to get our tests to pass(locally).
I holding off on some of the problems based on resolution of #3196368: [policy, no patch] Determine to which module the new security advisory functionality should be added because some stuff that are changes to Update module that now would need to be in System won't need to happen if we decide to move it back to Update module
Leaving at needs Review because I did push up some changes but there is still some open questions that will need work.
Comment #165
tedbowRegarding changes above, some big changes
Created
\Drupal\system\ExtensionVersionwhich is copy of\Drupal\update\ModuleVersionplus the new changes we needed for that class for this issue.I changed the name of the class because it was badly named to begin with. It is not just modules, but themes and profiles
Deprecated
ModuleVersionthough it is already internalModuleVersion. Removed dependence on the Update module for our testComment #168
tedbow`\Drupal\Tests\Scripts\TestSiteApplicationTest` was failing because it was attempting fetch the advisories at install time.
@alexpott suggested that tests should have to opt in to having the PSA advisories functionality enabled.
@mglaman helped me figure out how to do this!
Thanks to both and crediting
Comment #169
phenaproxima@tedbow and I have now iterated on this extensively and I think I'm out of things to complain about.
The scope is very clear: when there are critically important security announcements (which should be an exceedingly rare event), we want sites to nag their administrators about them. That's what this patch does, on most administrator-facing pages, and on the status report. Nice and clear.
I do, however, think the issue summary needs to be updated to reflect the changes that happened during development, including moving this functionality to System (rather than Update). So I'm marking this as needs work for that.
Comment #170
tedbowI am going postpone this on #3196368: [policy, no patch] Determine to which module the new security advisory functionality should be added
This will affect the issue summary
Comment #171
tedbowOk in #3196368: [policy, no patch] Determine to which module the new security advisory functionality should be added it was decided this should be in the system module
So the issue summary needs to be update and we probably need a documentation update for troubleshooting the advisories are not fetchable.
Comment #172
tedbowUpdated summary. Removed parts of about email notifications and pasted them into a comment on the follow up #3197333: Email site admins for security advisories displayed in Drupal so they won't be lost.
Comment #173
tedbowAdded not about how to disable the fetching of the advisories in settings.php to the release code snippet.
Comment #174
phenaproximaTried rewriting the issue summary for clarity and formatting. I hope it's still accurate!
Comment #175
phenaproximaRemoved a few follow-ups that I think are addressed in the merge request. Someone else should prune that list more comprehensively, though.
Comment #176
phenaproximaComment #177
phenaproximaComment #178
phenaproximaComment #179
tedbowIncluding just 2 edits to the description of determining if a advisories matches to include "development snapshot" because this is the only case it will happen.
to troubleshoot fetching. It is existing page that mentions the Update module but it should be the same for our use case.
Now that we are going to have Security Advisories fetching in the System module we need to update this to:
Comment #180
phenaproximaI've now reviewed this code many times and I think I'm out of things to complain about. This is well-scoped and does what it promises to do. From my perspective, it's ready. On the assumption that tests will pass, RTBC. Onward and upward to release manager review, and documentation updates before commit.
Comment #181
tedbowSo we still have Needs documentation updates tag
This doc page was made for #1538118: Update status does not verify the identity or authenticity of the release history URL and but not under the Update manager but System Requirements section. We should update this page to mention this new use case. There is a section about HTTP fallback but the new request does not have that feature. We probably don't want to have that here as that would open up another attack vector but comprising the request for the feed and linking to a fake advisory. Because advisories might be for non update reasons like needing make some server configuration change we should require HTTPS here otherwise a fake advisory could trick an admin into setting an unsafe server configuration change.
If we never needed a documentation page for failed requests for the Update XML until we convert the request to HTTPS in #1538118 I don't see why we would any extra documentation here.
system_help()has been updated and is linked where the advisories are displayed.Comment #182
dwwThe only reason we added the HTTP-fallback stuff to Update Manager was that people argued that not fetching anything at all for sites where PHP isn't properly configured to allow HTTPS was worse for security overall than the hypothetical man-in-the-middle attack vector. I wasn't entirely convinced, but I agreed to do the work on the fallback to make everyone happy and finally fix that ~9 year old critical bug.
However, we should either use that HTTP fallback consistently, or remove it entirely. This advisories feed is not inherently more important or risky than the release history feeds. If someone can compromise either feed, it's Really Bad(tm) for the site owner. A fake advisory that tries to trick an admin into setting an unsafe server configuration isn't really more dangerous than a fake feed pointing to fake updates with malicious code that the Update Manager might happily install for you. I don't think #181 justifies the exception. That reasoning is fundamentally right that the HTTP fallback is potentially risky, but then we should remove the HTTP fallback from Update Manager, too. I don't think this boils down to documentation updates. I think this either needs work in here to use the HTTP fallback, or we need a follow-up to remove the HTTP fallback entirely and not make a special exception for this feed.
Comment #183
tedbow@dww thanks for the context. I think that makes sense. I didn't think of the case that Update manager will use the XML to directly download update when the user submits the update. I agreed that that is more direct way to compromise the site.
If anyone had a case for not adding the fallback here the other option would be on failure in the status report page to an warning with a link security advisory listing and note they should follow this.
In the case of the Update module not getting the XML feed stops the site from using the Update module from downloading updates and via the UI.
But with the assumption that we do want the fallback luckily #1538118: Update status does not verify the identity or authenticity of the release history URL introduced a new setting
update_fetch_with_http_fallbackand does not rely on an Update module config setting we could use that setting here also. Obviously the that name might be a bit confusing since now it has 2 purposes but I think it would be less confusing than have 2 settings that control a fallback to HTTPS. I can't imagine where you would want to fallback for 1 but not the other.So we would at least need to update the comment in
default.settings.phpto mention this will affect the new advisory feed fetching also.We should probably at some point rename this setting maybe to
fetch_with_http_fallbackand deprecate the old name. Should just do that here or in a follow up?Comment #184
tedbowOur way of capturing log messages via
\Drupal\advisory_feed_test\TestSystemLoggerChanneldoesn't work for this test because we need to log `watchdog_exception()` calls which seem to not work with the decorated service. Added a simpler way to capture log messages but not sure that way will work for functional tests. So may need this 2 ways for the tests. I will to try to see if I can get to work with functional tests.I leaning towards the 2nd one but haven't added anything yet.
We should probably update Security advisory process and permissions policy which Drupal Security Team specific page
Comment #185
tedbowadded docs section to summary
Comment #186
tedbowmoved to User guide issue: #3203748: Add information about highly critical security advisories within Drupal
to follow-up
Comment #187
tedbowChanged the hook help the fail message to point to the new handbook page
We could get rid of the 2nd paragraph if we want because this is in the handbook page but I don't think it hurts to have it here.
The failure is message is the same as we added recently to the Update module except for the handbook page name. see
template_preprocess_update_fetch_error_message()Comment #188
jhodgdonOne other Docs To Do on this issue would be to add a Help Topic about this (or add information to an existing help topic). My guess is that it's not desirable to have that in the main patch, as Help Topics are still Experimental. (Not sure about that?)
Comment #189
xjm<a href=":url">More information on critical security advisories</a>.<a href=":url">View all security announcements</a>.In a similar vein, we should not mention "Drupal". (I confirmed with @webchick that it is policy to not use "Drupal" in the user interface so that distros can easily rebrand core.) We can just drop "within Drupal" as well.
With the above changes to reduce the verbiage, I think the paragraph linking the full feed is worth having both in the in-module help and on the handbook.
How about "Troubleshooting" instead of "debugging"? The former is more user-oriented, and we're not actually giving them debugging instructions; all the suggestions are more high-level. Edit: We would change the word in the handbook page as well.
We should add the information about how long the announcements are displayed to the handbook page as well.
WRT whether to address Help Topics, I think either on-issue or in a followup is fine. In the case of this issue a followup might be preferable, both because this issue is already so long, and because I think the help topic might have a broader scope of things from other modules related to security and/or updates. Thanks @jhodgdon!
NW for the various text fixes.
Comment #190
tedbowComment #191
xjmNW for my suggested docs changes on the MR; it was not actually fixed in the linked commit.
Comment #192
xjmHiding all patch files since they are very out of date now that we are working on the MR.
Comment #193
tedbowre #191 I misse MR comment before and was going off #190.
should be fixed now
Comment #194
tedbowAPI Changes in summary
Comment #195
tedbowComment #196
dwwThanks for all the recent effort in here! Haven't looked closely at the MR, but it seems to be very close.
Came across #2378513: Create system for distributing pre-announcements of extreme security issues in core/contrib while triaging, which I just marked as a child of this. Dunno if we should credit those folks here, or there, or what. Please advise (or do the deed). ;) Thanks!
Comment #197
jhodgdonI reviewed the API docs in this patch, and made a small suggestion about one spot. The rest looks good to me! (I did not look at the test modules/classes very carefully.)
Comment #198
tedbowAdded a change record https://www.drupal.org/node/3206473. improvements welcome
Comment #199
phenaproximaThis is a complex merge request to follow, so it's possible I missed a spot or two. But as far as I can tell, all feedback has been addressed, and all questions have been answered. Documentation appears to be up-to-date, and the remaining tasks in the issue summary appear to be done. Tests are passing. Best I can figure, this is ready.
Comment #200
catchA handful of questions/comments in the PR, nothing blocking.
One extra point:
Related to this, do we have a follow-up to e-mail the site admin e-mail address for PSAs the same as we do for security releases? We'd have to track e-mails sent somehow to avoid sending the same thing multiple times.
Comment #201
larowlanJust spent 30 minutes reviewing the last patch only to find it was converted to a merge request. Should we hide the patch files?
Copied over the comments that still applied
Comment #202
phenaproximaGood point, @larowlan. Hiding the defunct patches.
Comment #203
webchick@tedbow and @phenaproxima gave me a live demo of this patch (I didn't review the code because by the time we finished, @larowlan and @catch were looking at the code :)).
Here's my understanding; let me know if I'm wrong about any of this.
In a hypothetical recurrence of a SA-CORE-2018-002 type of situation, the following would happen:
1. A few days before the Wednesday security release (let's say, Sunday night), a Drupal security team member would run a script somewhere to update https://updates.drupal.org/psa.json with a pointer to a PSA node, like https://www.drupal.org/psa-2018-001, with its
is_psaflag set totrue.This is done manually, versus just sucking in everything from https://www.drupal.org/security/psa, so that the notices going through this system are truly critical and things all site owners should be aware of. (Which is good, because this code is in System module, and though possible to opt out of, is not something we want people to do or it undermines the entire point of this feature.)
2. All Drupal 8.9.15+ and Drupal 9.2+ and Drupal 10+ sites out there would receive notice of said PSA, visible to users with the "administer site configuration" permission, in both the Status report page, as well as any admin/ path on their site, on the following page load and all subsequent page loads. This message would be a "warning" style, because there is nothing they can actually do about it, yet. (The only way to turn off these messages is to set a configuration setting to opt out of the feature entirely, which once again, we really do not want people to do.)
(I have some questions about performance? what happens if drupal.org is down? do we cache this feed so as not to incur server hits? etc. but they are mostly irrelevant to this improvement; more on the backend side, and just points of curiosity.)
3. On Wednesday, the release is cut, a security team member updates the the JSON feed to the SA (vs. PSA) at https://www.drupal.org/sa-core-2018-002 with its
is_psaflag set tofalse. The notice on all Drupal 8.9.15+ and Drupal 9.2+ and Drupal 10+ sites turns from "warning" to "error" and they are linked to the SA with instructions on what to do. This time the message stays there until you do something to move off of the insecure version of core/your plugin, then it disappears.4. At some future date (the end of the week? the following week? 6 months later?) the JSON feed is updated once again to remove mention of the critical PSA. This is so these don't stack up over time.
---
Assuming all of that holds...
a) Our #1 goal from a security POV is that people don't turn off or otherwise work around this feature. That means keeping these messages as infrequent and as relevant as possible. If the outcome is IT help desks getting pinged by 300 people across their university (for example) suddenly seeing these messages, they're gonna get blitzed real quick. :\
Infrequency I believe is addressed by this being a manual addition process, separate from https://www.drupal.org/security/psa, "only break in case of emergency." So +1 for that. Better would be a "dismissable message" paradigm for the 2+ days that these messages are sitting there and you can do literally nothing about them, but that's not this feature's problem to solve. (Filed #3211534: Add support for dismissable messages for that, since I couldn't find an existing issue for some reason?)
For relevancy, reviewing the docs at https://www.drupal.org/docs/8/update/automatic-updates#s-public-service-... what I expected was that a PSA like https://www.drupal.org/psa-2019-02-19 would only show up on 8.5.x and 8.6.x sites. But this is apparently not how it works. The "insecure" array in the JSON schema is apparently ignored for PSAs, so I'll see it even if I'm running Drupal 9.
I don't think the lack of this should block the feature, but I do think it's an important consideration. We do not want our first time using this system to end up being a "boy who cried wolf" situation, causing lots of people to get the impression that this system nags them about irrelevant things and either ignore it or turn it off, so that the next time we need them to actually pay attention, they do not.
Filed #3211532: Add metadata to psa.json feed to allow only showing relevant core notices for that.
b) We need to remember that most people who maintain Drupal sites do so in addition to 50,000 other things they do, perhaps dozens of other technology platforms they support, etc. We all know what a string like "PSA-2018-10-17" means, but for someone new to Drupal, or someone who only touches their Drupal site once every 2 years, they very much might not.
So I was surprised when, in contrast with what's shown in the issue summary, the output on /admin pages is just barfing titles directly, with no extra context around it, like "Critical security announcements." There is a link to a help page, but the help shows up kinda halfway down the page with no special styling, and had @tedbow not pointed it out, I would never have found it. :\
I do not think it's reasonable to put the expectation on the security team to wordsmith these titles vs. just getting the information out in the event of a crisis. So a suggestion (does not need to hold up the patch): Just go back to the old labeling shown here:
@tedbow couldn't remember why that was removed. If there's a general feeling that it's not explanatory enough, could also put a [more info] link next to the title that links to the help text.
c) (also non-blocking curiosity) Assuming #4 above is true, why ever get rid of PSAs from the feed? If I haven't looked at my Drupal site in months/years, why not tell me about all of the things that need my attention? This is what Windows does, for example: https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Ffilestore.co...
----
Apart from that, this seems like a GREAT improvement that will help us tremendously down the line if another major issue happens that we need to get the word out about. Awesome work, everyone!
Comment #204
catchThe JSON is stored in key value expirable, and refreshed every 12 hours, however you've just made me think of something else... will comment on the PR.
Comment #205
tedbowThanks for the feedback everyone. I going through the MR comment now and will push up changes.
@webchick re
i found the commit it was removed in. I will try to match that to date of comments here or in the MR and see if was taken out for reason or if it was mistake.
Comment #206
tedbowIt was done in this commit where I was changing the logic where if there are any non-PSA advisories then all the messages should be shown as errors, so they are grouped together. I looked for comments around the date of this commit asking for it be removed and I don't see any.
Adding screenshots to summary
Comment #207
tedbowfixing image link
Comment #208
tedbowunsigning myself because addressed all current feedback on MR
Comment #210
aaronmchaleGenerally agree with everything in @webchick's review at #203.
Maybe I missed it, but at what point does an email get sent with the notice, and do we have a screenshot of what that email would look like (thinking along the lines of webchick's point about the context of the message that's displayed in the UI conveying the right information)?
Comment #211
phenaproximaEmail will be implemented in #3197333: Email site admins for security advisories displayed in Drupal, not this issue. :)
Comment #212
aaronmchaleAh yes, thank you, I had a vague recollection of it being discussed somewhere but forgot it was in a follow-up :)
I guess my only other thought, relates to webchick's review:
Is "administer site configuration" the right permission, it might be, but should be have a separate permission for this, so in the event a site administrator isn't clear who exactly will see these messages or wants to restrict it to those who can actually perform upgrades, a distinctive permission might address that.
It's reasonable to assume that a site might be setup in such a way where an administrator has the permission "administer site configuration", but that same administrator is not able to perform upgrades.
Consider the Managed Drupal Scenario: the provider/developer (who the client pays to manage the site) has every permission, but there exists a "limited administrator" role that the client has, which allows them to do things like change the site name, but not install modules or perform upgrades. This kind of messaging would not be relevant to the "limited administrator" because they can't do anything about it, it might just cause confusion and undue concern for them. After all, they are paying someone else to take care of upgrades for them so they don't have to worry about it.
Comment #213
tedbow@AaronMcHale thanks for reviewing the issue
re
and
This is already an existing pattern in core.
For instance if the Update module is enabled these users will see notifications of available updates including security updates even though they can't actually perform the updates in the UI. It seems unreasonable that we would show these users available updates and not alert them to the fact that one of these updates is actually a highly critical, once in every couple years kind of update that is of utmost importance. It also seems unreasonable that we would show them current security updates and not warn them that highly critical update will be available soon via PSAs in the UI.
The fact is we don't know that they can't perform updates just that they can't do it through the Update module. Since Composer is the recommended way to update Drupal core and modules often the Update module won't be enabled or if it is enabled you still might not want to assign the "Administer software updates" to any users because you don't want any user to update via the UI. Updating Drupal core is also not supported through the Update module and will not work for any module that has Composer dependencies.
With the Update module enabled currently "administer site configuration" permission is enough to
If we assume the Update module will not always be enabled or even it is the "Administer software updates" permission does not control how many sites, and all sites for Drupal core itself, run updates then we have to pick a permission to control showing these alerts to. Because "administer site configuration" is a restricted permission and should not be given to people that are not trusted and have responsibility on the site I think this is a good permission to choose and don't see an alternative permission.
for instance they can already:
Comment #214
aaronmchaleRe @tedbow in #213: Thanks for that very detailed explanation Ted, I think that all makes sense and I agree with your rational. I think if (as you said) there is already an existing pattern/precedent here then "administer site configuration" sounds like the right permission for this issue to target.
I do wonder though if a follow-up issue should be opened to look more broadly at if there should be a separate permission for everything to do with updates, so not just these PSAs, but the available update reports and other areas. You highlighted an important point, we cannot know which users are actually able to perform updates; I worry that the "administer site configuration" permission could become quite broad in its scope, and I could reasonably envision a site that might want to separate the administration of configuration from everything to do with updates, so exploring that further in a follow-up issue I think would be valuable.
But, as I said, I think for this issue, I agree with everything you've said.
Comment #215
phenaproximaAdding #3212017: Use the flood system to rate limit security advisories fetching as a related issue.
Comment #216
phenaproximaI think all committer feedback so far has been addressed. (Mostly this consists of @tedbow replying to explain the reasoning behind certain things.) I think this is ready for a committer to look again, so restoring RTBC.
Comment #217
larowlanRemoving 'Needs release manager review' on the basis of #200
Updating issue credit
Comment #218
larowlanI had hoped to review this in time to make the alpha deadline, but because of various work commitments I missed the window.
This looks good to go to me.
Comment #219
phenaproximaOpened #3213461: [policy, no patch] When should service arguments be wrapped? to discuss the possible coding standards change suggested by @larowlan.
Comment #221
larowlanI discussed this with catch and xjm around the release date of 9.2.0-alpha1.
Both agreed this was fine to go into 9.2 between alpha and beta because of the strategic importance.
I reviewed this at the time, but just missed the alpha-freeze cut-off.
Committed 79dd832 and pushed to 9.3.x. Thanks!
Backported to 9.2.x per the above.
For all those involved, great work!
Published the change record and unpostponed two issues.
Comment #222
tedbow🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊
@larowlan thanks! And thanks everyone who worked on this!
🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊🎉😊
Comment #223
jibranThis broke the drush site install command for my local dev. I'm using the latest drush release with the latest HEAD and PHP 8. I had to disable the event to install the site.
Complete error for the reference https://pastebin.com/X6SZtp49
Comment #224
larowlanwhat version of Guzzle are you running @jibran
Comment #225
benjifisherI was curious about the report in #223, so I tried to reproduce it, using Lando to install PHP 8.
In my test,
drush si standardworks fine..lando.yml:Some versions:
Comment #227
andypostPlease help review additions to help topics about added advisories #3204175: Add security advisories Help Topic