Problem/Motivation
Menu router items don't support %-style or @-style placeholders for custom title callbacks using t(). Thus check_plain()ed is always used. This doesn't support the kind of functionality needed.
It was noted that this is a regression from D5 which never got corrected in D6.
Proposed Resolution
- Convert some of these page callbacks into actual title callbacks.
- Resolve without losing built-in securing or making API changes
Remaining tasks
- Determine whether #1 of the proposed resolution is still a valid resolution given #2 of proposed resolution.
- Determine whether latest patch addresses #2of the proposed resolution
- Determine what needs to be used in order to change local task titles.
- Reroll sun's patch, hopefully in a way that the bot passes.
- Add conversions to patch
- Test the rerolled patch.
Original Summary by sun
Offshoot from #550228: Configuration page: Media category:
Menu router items cannot use %-style or @-style placeholders in 'title arguments' for custom 'title callback's that are using t(). The result is always check_plain()ed.
Defining 'options' => array('html' => TRUE) for menu router items is also not taken over.
We have many other places in core where we need custom title callbacks that process and convert a menu loader argument into proper arguments for usage in t(), and all of those currently use drupal_set_title() in the 'page callback' of the menu router item directly.
| Comment | File | Size | Author |
|---|---|---|---|
| #91 | 556910-Menu_router-91.patch | 27.54 KB | ZenDoodles |
| #91 | translation.pages_.inc_.rej_.txt | 537 bytes | ZenDoodles |
| #91 | system.module.rej_.txt | 2.87 KB | ZenDoodles |
| #91 | statistics.pages_.inc_.rej_.txt | 5.52 KB | ZenDoodles |
| #83 | drupal.menu-title-passthrough.83.patch | 43.52 KB | sun |
Comments
Comment #1
webchickFixing version. We added PASS_THROUGH in D7.
Also labeling as a regression and re-filing as a bug report, since this was possible in D5.
Comment #2
sunoh, I wanted to leave a pointer to this example function: http://api.drupal.org/api/function/node_page_edit/7
Comment #3
sunoops
Comment #4
chx commentedWhat about something like this?
Comment #5
sunHmm. If we add a new property, why don't we use
'title option' => PASS_THROUGHor similar?Although I can see the same problem happening with code like added in the test, the original bug report was rather about
This review is powered by Dreditor.
Comment #6
chx commentedWhether we use title options or not is up for discussion. I have amended the patch to support any title callback not just t.
Comment #8
chx commentedComment #9
pwolanin commentedI'd rather see more information in callback arguments - putting a leading "-" in the callback name just seems very unintuitive and odd.
Comment #10
sunAs the original post explains, my first consideration and assumption was:
"oh, right, I probably need to specify
'options' => array('html' => TRUE)for the menu router item in hook_menu()..."Which didn't work. But, would that be a feasible way? I think it would be the most logical approach, since we're basically in the domain of menu links here.
Comment #11
pwolanin commentedI think in a sense we can alrady do that. Perhaps the problem really lies in this function:
http://api.drupal.org/api/function/_menu_item_localize/7
Some links are getting passed through a callback - but not all, plus the fact that we are later calling check_plain() on everything for the page title. Perhaps we should assume that any callback makes the text safe, and we should call check_plain in any case where there is not a callback?
Comment #12
chx commentedNote that the definition does not use - in the callback name it's only an internal trick.
Comment #13
Freso commented#8: title_pass.patch queued for re-testing.
Comment #15
tanoshimi commentedApologies for jumping in on this thread, but it looks like this might relate to my question posed at: http://drupal.org/node/725524
_menu_item_localize is responsible for translation of both title and description of a menu item, but looking through http://api.drupal.org/api/function/_menu_item_localize/7 , it seems that there is different functionality provided for each, namely:
- Menu titles can specify a custom 'title callback', and that callback (or the default callback, t()) can receive additional 'title arguments'.
- Menu item descriptions, however, always only use t(), and cannot have additional parameters supplied.
This discrepancy seems like a regression too, since previously it was possible to provide inline arguments to both menu item titles and descriptions, such as this:
So, is there a case for adding a 'description callback' and 'description arguments' to menu items as well? Should I file that as a new regression bug report? (Sorry, I've not done much to drupal core before... trying to follow protocol!)
Comment #16
dmitrig01 commentedThat's a feature. We're focused only on this bug here.
Here's a reroll. Not sure how useful this is.
Comment #18
berdirRe-roll, #16 was just missing the setUp() call to enable menu_test.module.
Not sure if I like the approach, using a constant as a key in hook_menu() is uncommon, all other keys are just strings. Also, the - trick seems quite hacky but I can't provide a better solution.
Comment #19
berdirComment #20
pwolanin commentedAs above, I don't think that's the right approach. I'd do something more like this.
Needs a test or two.
Comment #22
pwolanin commentedAdded test changes, and changed path.inc
Comment #24
pwolanin commented#22: title_pass_556910-22.patch queued for re-testing.
Comment #26
berdirI think those DBLog tests happen to fail sometimes... let's try again.
Edit: Oh, pwolanin already tried that. Maybe not then :)
Comment #27
berdir#22: title_pass_556910-22.patch queued for re-testing.
Comment #29
Scott Reynolds commentedOk fixed the tests. Couldn't repeat the file failures but the db log I did. Little scary how this works properly. I haven't figured out why this passes but it does.
Comment #30
Scott Reynolds commentedTest bot please test.
Comment #31
chx commentedThis will never be too pretty but works.
Comment #32
sunum, did we actually double-check every 'title callback' in core so that this is actually the case and we're not introducing countless of security issues here?
Powered by Dreditor.
Comment #33
pwolanin commentedIndeed - looks like we need some cleanup:
to be fixed:
http://api.drupal.org/api/function/_aggregator_category_title/7
http://api.drupal.org/api/function/filter_admin_format_title/7
http://api.drupal.org/api/function/menu_overview_title/7
http://api.drupal.org/api/function/node_type_page_title/7
http://api.drupal.org/api/function/user_page_title/7
already ok:
http://api.drupal.org/api/function/comment_count_unpublished/7
http://api.drupal.org/api/function/shortcut_set_title/7
http://api.drupal.org/api/function/taxonomy_term_title/7
http://api.drupal.org/api/function/taxonomy_admin_vocabulary_title_callb...
unclear - should this really be using t()?
http://api.drupal.org/api/function/field_ui_menu_title/7
Comment #34
dawehnerFixed:
http://api.drupal.org/api/function/_aggregator_category_title/7
http://api.drupal.org/api/function/filter_admin_format_title/7
http://api.drupal.org/api/function/menu_overview_title/7
http://api.drupal.org/api/function/node_type_page_title/7
http://api.drupal.org/api/function/user_page_title/7
Can someone explain why
is not used somewhere? I guess this function needs to be check_plained, too.
Regarding field_ui_menu_title, its not translated for example in field_ui_field_overview_form
Comment #36
sun@dereine: See #737792: Use a "title callback" for node/%node instead of drupal_set_title()
Comment #37
tstoecklerStraight re-roll. Will work on fixing the tests now.
Comment #38
tstoecklerLet's see what the bot says.
Comment #39
tstoecklerI just noticed that format_username() (which is called by user_page_title()) is used very inconsistently throughout core currently. Even though the documentation says it should be check_plain'ed, in a lot of places it's not. Bascially in this patch we have the choice of moving the check_plain into format_username() or into user_page_title(). I'm not really an expert on this, but from a practical standpoint in makes more sense to alleviate all security implications centrally (like in this patch) and deal with double check_plain'ing in a different issue, than keeping it the way it is and fixing all the security issues in a different issue. Doesn't really matter either way, though.
Comment #41
catchformat_username() is supposed to be used for non-html contexts - i.e. a plain text email, so I don't think it makes sense to check_plain() by default.
Comment #42
tstoecklerThis one should work. (Broken due to #692044: A site with statistics module enabled is much slower in serving cached pages in D7 than in D6)
Comment #43
tstoecklerComment #45
tstoecklerJust for the reference: I have no clue, why the bot couldn't install. I tried installing manually and it worked fine.
Running the SimpleTests with the patch applied fixed almost all problems (my machine is too slow, for me to actually run them all, but I tested about 10% and of those 95% were green).
Rerolled patch for #41 (#42 was a crosspost). No other changes. It would be ridiculous if this one doesn't break, but setting to 'needs review' just for the fun of it.
Comment #46
tstoeckler...
Comment #47
chx commentedI think we agreed on the approach already and tests pass.
Comment #48
yesct commented#45: drupal_556910-45.patch queued for re-testing.
Comment #49
webchickUgh. I really, really don't like this approach. This introduces a huge inconsistency into the API:
- When you call drupal_set_title('string'); it's automatically check_plain()ed for you. (a really great security improvement added in D7)
- When you define a menu title callback, you are now responsible for check_plain()ing it. (this was NOT the case in D6 (see menu_overview_title() for example), so we've just *lost* built-in security here.)
- Not to mention we're making API changes a bazillion years after code freeze, that have very strong security implications if they are not strictly followed.
Avoiding built-in security checks should be opt-in, IMO, same as it is when calling drupal_set_title() directly.
Let's discuss some other approaches.
Comment #50
naxoc commentedI am not sure I understand this bug. Is it that we want to allow html tags in the the titles - or is it that the placeholders dont work? I cant really replicate the placeholders not working, but I also may just not get the issue.
Using the example from http://api.drupal.org/api/function/node_page_edit/7 the placeholders work fine if I do something like this.
Is it the html-tag/placeholder mix-in we want or can someone clarify? I removed the em-tags - they will be checkplained, but placeholders work.
Comment #51
sunI think the resolution chosen in this patch is the right one, and just because there has been some built-in security -- which actually is the cause for this entire bug -- does not mean it was right.
I also do not see that much of a relationship between
drupal_set_title($unsafe_string)andreturn $safe_stringin technically unrelated locations.Technically, I'd say that it's instead correct to demand from menu title callbacks to return properly sanitized strings, as those strings are intended to be output. You have to sanitize everything else you want to return for output, so this is just consistent. The fact that drupal_set_title() automatically performs sanitation of the passed in string is definitely not something we should advance on. It's a constant source of problems and leads to developers making false assumptions about security.
Comment #52
webchickI disagree. Where developers consistently are Doing It Wrong (tm), I think it makes a lot of sense to make the defaults secure. We do this in lots of places, actually. drupal_set_title() in Drupal 7, theme variables, @ and % placeholders in t(). All of which were added for the very reason that people never remember to check_plain() their stuff, and the security team gets overloaded with XSS reports.
I support adding some sort of mechanism to opt-out of this automatic, default security, but I don't support regressing backwards to making it the developer's responsibility. Especially at this point in the release cycle.
Comment #53
pwolanin commentedI'm a bit torn here. title callback are way less common that drupal_set_title (though could that in part be due to this bug?).
As the instegator of the API change for drupal_set_title(), I'm much less bothered by requiring title callbacks to return safe text.
So, what so we have left? The hack chx suggests would work and avoids a schema change. Hell, we could even backport that to 6 if desired. It's ugly, but I guess only those peering into the innards of menu.inc would be offended.
Comment #54
dhthwy commentedTo nitpick webchick's comment:
In my most humble opinion, if a developer is consistently doing it wrong then there are much larger problems at play.
Security is great, but this is security that takes away needed functionality. If the consensus is the approach taken in this patch is the right way to do it, then any other approach will likely be a hack or just plain WTF. Any fix should solve the problem at its root which it looks like this patch does.
In any case, the bottom line to me is that sometimes you have to sacrifice some security for usability and functionality. Security gets sacrified all the time on the internet, if that weren't true, there would be no internet, as no one would run web servers or any other service because any service is a security risk. There is no such thing as a 100% secure service. And there are so many different ways a Drupal developer can open up their code to vulnerabilities if they don't learn to follow best security practices.
It looks to me like webchick doesn't think this issue is important enough to sacrifice any security, so this should just be a won't fix instead of some ugly hack that someone might *eventually* (who wants to write an ugly hack?) conjure up.
Comment #55
sunre @pwolanin #53:
I can definitely confirm that assumption - I've worked on a couple of contributed modules that would like to use a title callback, but are not able to do so because of this bug. AFAIK, even Drupal core cannot use title callbacks in a few locations.
The bug could not be fixed during D6, but it would still be possible and a good decision to fix it now.
Comment #56
chx commentedThis is the clean solution and we agreed with webchick that security trumps API and schema changes. Note that the API change is permissive: everything works as it did before but if you add a passthrough now you get new behaviour.
Comment #58
chx commentedThere is a fake entry for the home page in the active trail and that had no passthrough defined.
Comment #59
chx commentedAnd with less debug.
Comment #61
chx commented#59: menu_passthrough.patch queued for re-testing.
Comment #62
dhthwy commentedProbably not the right person to RTBC but here goes anyway.
Comment #63
dries commentedPersonally I think 'passthrough' is a terrible variable name. Passthrough what?
What about calling it 'escape' or something?
Comment #65
pwolanin commentedsee: http://api.drupal.org/api/function/drupal_set_title/7
I suggested to chx in IRC that the element/column be named something like 'title_output', and that we use the same 2 constants we defined for drupal_set_title, which are CHECK_PLAIN and PASS_THROUGH.
Comment #66
sunIf we go with this approach, then I'd like to re-reference and restate the original/opening post and #10, i.e., the very first logical assumption that crossed my mind when I tried to make it work without looking up the code:
Technically, this would also open up other possibilities (no comment).
--
While I'm ok with the proposed solution, I want to point out that there's hook_menu_alter(), and 'title callback' of module A may be replaced with an override by module B, and module A may change over time, requiring to set whatever option we conclude on here, but effectively making the combination of module A and module B insecure - a security interdependency that is very hard to track. Only if the title callback would return an array ('title', FALSE) or something, the reponsible title callback would be responsible for setting proper escaping options.
Comment #67
pwolanin commented@sun - I'm not sure the options array makes sense in this context - since in many cases the title is not a link (not processed via l()).
Comment #68
chx commented@sun also already you need to alter title/title callback/title arguments together, now we add title output.
Comment #69
chx commentedSo this is how we can allow PASS_THROUGH.
Comment #70
chx commentedErm, that's some patch mixup. Here's a better one.
Comment #71
pwolanin commentedIn the schema would it make sense to write:
Comment #72
chx commentedIt would.
Comment #73
sunIt would be really nice if those were defined once for all and could be retrieved through a helper function menu_router_columns($optional_column_prefix)
Elsewhere in core, we're already using a nasty Schema API trick to retrieve all columns from the schema, sometimes skipping the 'weight' column (clashes with ml.weight).
menu_router_columns($optional_column_prefix = 'm', $exclude = array())
would just be lovely.
Aside from that, RTBC.
Powered by Dreditor.
Comment #74
chx commentedThat's a followup issue isnt it?
Comment #75
pwolanin commentedYes, I don't think this patch needs to add such a helper function - though I agree it would make the code nicer throughout menu.inc.
Comment #76
dries commentedI looked at this patch and while the code is easy to understand and follow, it is a big ugly and, as webchick explained, inconsistent.
I'd like to better understand the use case. We didn't have this in Drupal 6, and as far as I can tell, we did fine without it. Menu titles can also overwritten by drupal_set_title(), and we have the option of using menu title callbacks (i.e. 'title callback' option in hook_menu()).
In other words, I'd like to better understand why this is a critical bugfix versus a feature request. I might be missing something but this looks like it actually might be a non-critical feature request. I'm setting this back to 'needs review' so we can discuss the critical use cases a bit more as that would help me, and other reviewers, understand the issue better.
Comment #77
chx commentedWell, webchick review's was for another approach. this patch defaults to CHECK_PLAIN. Also, no matter what you do with a title callback you still will be check_plain'd in D6 and HEAD:
$title = check_plain(menu_get_active_title())indrupal_get_title. As for inconsistent, rather it becomes more consistent with the new argument ofdrupal_get_title-- it even reuses the same constants.Why critical, it's a regression from D5, and a long standing one because we did not fix this in D6.
Comment #78
sun- webchick's main concern against this patch has been resolved in the meantime.
- #1 (also by webchick) applies, this is still a regression from D5. There are actually a couple of places in core + many contrib modules where we'd like to use
t('Edit %title', ...), but currently cannot, because the %-placeholder would be double-escaped. This forces page callbacks to manually invoke drupal_set_title(), although manually overriding the page title should be rarely needed.Comment #79
dries commentedThis forces page callbacks to manually invoke drupal_set_title(), although manually overriding the page title should be rarely needed.
Calling drupal_set_title() from a page callback is not a crime, as far as I'm concerned, and adding 'title_option' to the menu system is a bit ugly and less transparent.
It would be good to see what page callbacks we'd change. Let's include some conversions in this patch, please. This will provide reviewers some actual real-world examples.
Based on chx's and sun's response, this sounds like a 'major' issue at most, and certainly not a 'critical' issue. I'll wait with demoting this patch for now so we can discuss it some more.
Comment #80
pwolanin commentedtrying to do a couple example conversions locally, but getting an unexpected warning for node/add/X
# Warning: htmlspecialchars() expects parameter 1 to be string, array given in check_plain() (line 1500 of /Users/Shared/www/drupal-7/includes/bootstrap.inc).Comment #81
moshe weitzman commentedRelying on drupal_set_title() during the page callback will give incorrect titles in Shortcut module AFAIK.
Comment #82
moshe weitzman commentedDowngrade as per Dries in #79
Comment #83
sunAttached patch converts most page callbacks to use proper title callbacks.
However, after doing this, I realized that we still need to "disable" title callbacks for local tasks, which should always use 'title' only. Dynamic or conditionally changing local task titles can be achieved via http://api.drupal.org/api/function/hook_menu_local_tasks_alter/7, but are fairly uncommon either way. (Comment module containing the only code I've ever seen with regard to local tasks.)
Thus, this patch works, but requires to fix titles of local tasks (make them statically use 'title' only), which we've discussed months ago already.
Comment #85
sun.core commentedThis issue is a real problem for modules like Shortcut and others that are trying to present menu links with a human-readable meaning without having a menu tree at hand. We have a technically ready patch, but the actual conversion of (un)certain router items seems to trigger failing tests.
Comment #86
chx commentedComment #87
sunComment #88
Zgear commentedI'm going to make an issue summary first, once I have a clear insight on this issue I believe I shall tackle it :)
Comment #89
Zgear commentedOk I believe that it needs more work but it will have to do for now.
Comment #89.0
Zgear commentedUpdating Issue summary with details from comments
Comment #89.1
Zgear commentedUpdated issue summary.
Comment #90
ZenDoodles commentedWell done on the issue summary micnap.
Comment #91
ZenDoodles commentedReroll for D8...
Well, a start anyway. Also attached the .rej files I did not get finished yet in case I don't get back to it in time.
Might as well get the failing tests out of the way too, but should go back to needs work when the bot is done with it.
Comment #93
tim.plunkettThis isn't a true regression if it wasn't in either of the two current releases of Drupal.
Comment #94
tim.plunkettThis tries to do a lot in one patch, how about just making the fix and one test usecase of it, the individual conversions are what keep breaking the patch, and they could be done elsewhere.
Comment #95
yesct commentedmight be novice to do #94
Comment #96
heddnThis might be more of a novice task if I could tell what was part of just making the fix and what was part of the conversion work.
Comment #97
yesct commented@ZenDoodles seemed like this might be up for grabs. :) unassigning it.
Comment #97.0
yesct commentedAssigment from Core Office Hours.
Comment #98
Yaron Tal commentedIssue is probably not novice anymore.
patch is way to old to apply, hook_menu and drupal_get_title() no longer exist.
Also this might be possible somehow now, because Drupal\Core\Controller\TitleResolver::getTitle() has some code in there that supports variables in page titles.
Comment #99
dawehnerThis is not true any longer, the title resolver actually has support for it now.