Skip to content

[5.4] Escape csv formula characters in banner tracks export#48047

Open
arib06 wants to merge 9 commits into
joomla:5.4-devfrom
arib06:banners-tracks-csv-formula
Open

[5.4] Escape csv formula characters in banner tracks export#48047
arib06 wants to merge 9 commits into
joomla:5.4-devfrom
arib06:banners-tracks-csv-formula

Conversation

@arib06

@arib06 arib06 commented Jul 4, 2026

Copy link
Copy Markdown

Pull Request resolves # .

  • I read the Generative AI policy and my contribution is either not created with the help of AI or is compatible with the policy and GNU/GPL 2 or later.

Summary of Changes

The banner tracks CSV export in TracksModel::getContent() writes banner_name, client_name and category_title into the file with only quote-doubling. Those values are user controlled (anyone with com_banners create/edit can set a banner or client name), so a name beginning with =, +, - or @ lands in a cell that a spreadsheet evaluates as a formula when an administrator opens the exported file. com_actionlogs already escapes its CSV export this way; this applies the same leading-space escaping to the tracks export.

Testing Instructions

  1. Create a banner client (or banner) whose name is =1+1 (or =HYPERLINK("http://example.com","x")).
  2. Generate at least one impression/click so the banner has a track row.
  3. Go to Components -> Banners -> Tracks and export to CSV.
  4. Open the downloaded CSV in LibreOffice Calc or Excel.

Actual result BEFORE applying this Pull Request

The name cell is treated as a formula and evaluated by the spreadsheet.

Expected result AFTER applying this Pull Request

The value is prefixed with a space and shown verbatim as text, matching how com_actionlogs exports are handled.

Link to documentations

Please select:

  • Documentation link for guide.joomla.org:

  • No documentation changes for guide.joomla.org needed

  • Pull Request link for manual.joomla.org:

  • No documentation changes for manual.joomla.org needed

@alikon

alikon commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

the same issue can arise in other place, for example User Action Log export

@arib06

arib06 commented Jul 4, 2026

Copy link
Copy Markdown
Author

Good point to check. User Action Log export already runs its fields through ActionlogsHelper::escapeCsvFormula (added back in 3.9.7), so it's covered - this PR just brings the banner tracks export in line with that same handling. I grepped the tree for other CSV exports and those two are the only endpoints that build a download, so nothing else is left unescaped.

@alikon

alikon commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

shouldn't be better than to use the same method all over without duplicates?

@arib06

arib06 commented Jul 4, 2026

Copy link
Copy Markdown
Author

Makes sense. Dropped the duplicate helper and made ActionlogsHelper::escapeCsvFormula public so the tracks export calls the same method. Pushed.

@richard67

Copy link
Copy Markdown
Member

@arib06 Please conform that you have read the Generative AI policy and your contribution is either not created with the help of AI or is compatible with the policy and GNU/GPL 2 or later by checking the check box at the top of your PR description. Thanks in advance.

@richard67 richard67 changed the title escape csv formula characters in banner tracks export [5.4] Escape csv formula characters in banner tracks export Jul 4, 2026
Comment thread administrator/components/com_actionlogs/src/Helper/ActionlogsHelper.php Outdated
…onlogsHelper

Signed-off-by: mohammed arib <arib@bugqore.com>
@arib06

arib06 commented Jul 5, 2026

Copy link
Copy Markdown
Author

@richard67 Read the policy and ticked the box, it's GPL2-compatible. @joomdonation reverted the visibility change too and moved the escaping into BannersHelper so com_banners doesn't depend on the Action Logs helper.

Comment thread administrator/components/com_banners/src/Helper/BannersHelper.php Outdated
@LadySolveig

Copy link
Copy Markdown
Contributor

Given that we’re ourselfs then using it identically in two places and it might be also helpful to third-party extension developers, could be an idea to have it as output filter.
https://github.com/joomla/joomla-cms/blob/5.4-dev/libraries/src/Filter/OutputFilter.php

What do you mean @joomdonation ?

Comment thread administrator/components/com_banners/src/Helper/BannersHelper.php Outdated
Signed-off-by: mohammed arib <arib@bugqore.com>
@arib06

arib06 commented Jul 8, 2026

Copy link
Copy Markdown
Author

Happy to do that. Moving escapeCsvFormula into OutputFilter as a shared method means com_actionlogs and the tracks export both call the same code, and it's exposed to third-party extensions too. It also sidesteps the earlier cross-component dependency concern, since OutputFilter lives in the core library rather than in one component. If that's the direction you both prefer, I'll add it there and update com_actionlogs to use it.

@richard67

Copy link
Copy Markdown
Member

@arib06 i would assume that the issue only happens when you directly open the CSV with Excel by double click. This is not recommended anyway, the file should be imported into Excel from text. Can you confirm that the issue does not happen when you use the data import from text in Excel?

@richard67 richard67 self-assigned this Jul 8, 2026
@arib06

arib06 commented Jul 9, 2026

Copy link
Copy Markdown
Author

@richard67 Yes, that's right. The formula only evaluates when the file gets parsed automatically, which is what a direct double-click open does since .csv is associated with Excel by default. If you go through Data > From Text/Get Data and set the column type to Text, the value comes in verbatim and isn't evaluated, so that import path is safe.

The reason it's still worth escaping is that the double-click open is the common path for an admin grabbing a download, and LibreOffice Calc evaluates on open as well. This is the standard CSV formula injection case (CWE-1236), and the leading-space fix here is the same handling com_actionlogs already ships, so it's just bringing the tracks export in line rather than a new approach.

@LadySolveig

Copy link
Copy Markdown
Contributor

I don’t quite understand what role this plays. The fix was already implemented elsewhere some time ago, and the behaviour is now inconsistent in parts of the CMS.

com_actionlogs and the banner tracks export now call the same
OutputFilter::escapeCsvFormula, and third-party extensions can reuse it.
@arib06

arib06 commented Jul 11, 2026

Copy link
Copy Markdown
Author

@LadySolveig Agreed, the inconsistency was the real issue here. I've moved the escaping into OutputFilter::escapeCsvFormula in the core library and pointed both com_actionlogs and the banner tracks export at it. The duplicate helper I'd added to BannersHelper and the private copy in ActionlogsHelper are gone, so there's one implementation now and it's exposed to third-party extensions as well. Pushed.

@LadySolveig

Copy link
Copy Markdown
Contributor

@arib06 , your last commit was perhaps a bit rushed.
This is an architectural decision that needs to be made by the maintenance team, and was just a suggestion on my part to brainstorm.
Quite apart from that, I don’t think it could be included in a patch release of the 5.x series as a bug fix in that form, but would have to wait until version 6.2 at the earliest, if at all.

I think best would be to wait for further feedback here before you invest any more time and effort.

@alikon

alikon commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

imho
as long as this pull request have no backward impact,
it reduce code duplication,
fix a potential bug,
so not sure it's a candidate for wait untill 6.x,
but maintainers can have different opinion

Signed-off-by: mohammed arib <arib@bugqore.com>
@arib06

arib06 commented Jul 11, 2026

Copy link
Copy Markdown
Author

Addressed the review: instead of removing the old escapeCsvFormula method and its $characters property from ActionlogsHelper I've marked both @deprecated (5.4, to be removed in 7.0) with a pointer to OutputFilter, so nothing is dropped from the API and BC is preserved.

On the OutputFilter move itself, understood that the shared-method direction and the target version are the maintenance team's call. I'm happy to hold here and not touch it further until there's a decision on whether it goes in a 5.x patch or waits for a later release. If you'd rather I pare this back to just the banner tracks fix and leave com_actionlogs untouched, say the word and I'll do that.

Comment thread administrator/components/com_actionlogs/src/Helper/ActionlogsHelper.php Outdated
Comment thread administrator/components/com_actionlogs/src/Helper/ActionlogsHelper.php Outdated
@alikon

alikon commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

I have tested this item ✅ successfully on 4cf3802


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/48047.

@richard67

Copy link
Copy Markdown
Member

On the OutputFilter move itself, understood that the shared-method direction and the target version are the maintenance team's call. I'm happy to hold here and not touch it further until there's a decision on whether it goes in a 5.x patch or waits for a later release. If you'd rather I pare this back to just the banner tracks fix and leave com_actionlogs untouched, say the word and I'll do that.

@arib06 Meanwhile we have discussed your PR in the maintainers team and came to the conclusion to do the fix in 2 steps:

  1. A quick, fully backwards compatible (b/c) fix in 5.4-dev with this PR here (which will be merged up into 6.1-dev when it has been merged) so it can be fixed with a 5.4.x and 6.1.x patch version.
  2. Later in a new minor version (currently 6.2-dev) the clean up of duplicate code by adding a new method to the OutputFilter, but not the OutputFilter from the CMS, it should be done in the OutputFilter of the framework because we will move the usage of the filter classes in the CMS from the CMS to the framework classes.

For 1. this means you should do the "pare this back to just the banner tracks fix and leave com_actionlogs untouched" thing as you supposed, but in the banner tracks it should be a private and not a public function for b/c (adding a private method does not break classes extended from that, while adding a public method would break that).

For 2. it means that you could create a PR for the framework here https://github.com/joomla-framework/filter/pulls for the 4.x-dev with the new method in file src/OutputFilter.php like you had added it here to file libraries/src/Filter/OutputFilter.php.

When 2. will be done and a new 4.x release of the filter framework has been made, it will then need a third step:
This will be a PR for the 6.2-dev branch here with the composer update for the filter framework and the following changes in additon:

  • Change the function calls in banners and action logs to the new framework function.
  • Remove Banner::escapeCsvFormula which was added with this PR here - this is fully b/c as only a private function is removed
  • Deprecate Actionlog::escapeCsvFormula for removal in 8.0 - here we have to go this way because the function is public

Could you do the first 2 steps? That would be much appreciated.

@arib06

arib06 commented Jul 20, 2026

Copy link
Copy Markdown
Author

@richard67 Both done.

  1. Pared this PR back to the banner tracks fix only. escapeCsvFormula is now a private method on TracksModel, and the ActionlogsHelper and CMS OutputFilter changes are reverted, so the diff is a single file and fully b/c.

  2. Framework PR for the shared method is up against 4.x-dev: Add OutputFilter::escapeCsvFormula joomla-framework/filter#88. It adds OutputFilter::escapeCsvFormula() plus unit tests in Tests/OutputFilterTest.php.

Happy to do step 3 here against 6.2-dev once that framework release is out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants