Skip to content

campaign parameters now masked with CNIL active#213

Merged
AltamashShaikh merged 5 commits into5.x-devfrom
dev-20125
Apr 29, 2026
Merged

campaign parameters now masked with CNIL active#213
AltamashShaikh merged 5 commits into5.x-devfrom
dev-20125

Conversation

@nathangavin
Copy link
Copy Markdown
Contributor

@nathangavin nathangavin commented Apr 23, 2026

Description

Masks campaign parameter values when the CNIL policy is enabled. Refer to matomo-org/matomo#24416 for more information.

Backwards compatibility is maintained, if the setting is not available then all new functionality is disabled.

Checklist

  • [✖] Tested locally or on demo2/demo3?
  • [✔] New test case added/updated?
  • [NA] Are all newly added texts included via translation?
  • [NA] Are text sanitized properly? (Eg use of v-text v/s v-html for vue)
  • [✖] Version bumped?
  • [✔] I have understood, reviewed, and tested all AI outputs before use
  • [✔] All AI instructions respect security, IP, and privacy rules
  • [✖] Documentation updated?

@nathangavin nathangavin marked this pull request as draft April 23, 2026 06:14
@nathangavin nathangavin marked this pull request as ready for review April 24, 2026 05:23
@nathangavin nathangavin requested a review from a team April 24, 2026 05:24
@Chardonneaur
Copy link
Copy Markdown
Contributor

Hi @nathangavin
i have a remark:
"Blocking UTMs without polluting traffing sources seems to work as expected but I am worried that we cannot enable UTMs tracking again through a piece of code (user case : on explicit consent)"

Comment thread Columns/Base.php Outdated
{
$campaignDetector = StaticContainer::get('advanced_campaign_reporting.campaign_detector');
$campaignParameters = MarketingCampaignsReporting::getCampaignParameters();
$campaignValuesMasked = MarketingCampaignsReporting::isCampaignValuesMaskingEnabled((int) $request->getIdSiteIfExists());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$campaignValuesMasked = MarketingCampaignsReporting::isCampaignValuesMaskingEnabled((int) $request->getIdSiteIfExists());
$isCampaignValuesMasked = MarketingCampaignsReporting::isCampaignValuesMaskingEnabled((int) $request->getIdSiteIfExists());

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment thread Columns/Base.php Outdated
$request,
$campaignParameters
);
$campaignDimensions = $this->maskDetectedCampaignDimensions($campaignDimensions, $campaignValuesMasked);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$campaignDimensions = $this->maskDetectedCampaignDimensions($campaignDimensions, $campaignValuesMasked);
$campaignDimensions = $this->maskDetectedCampaignDimensions($campaignDimensions, $isCampaignValuesMasked);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment thread Columns/Base.php
Copy link
Copy Markdown
Contributor

@AltamashShaikh AltamashShaikh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nathangavin More from AI

  - The combined Source - Medium report can still expose raw placeholder labels. The new formatting hook only applies to dimensions inheriting this plugin’s Columns\Base, but CampaignSourceMedium
    extends plain Dimension and has no formatValue() override in Columns/CampaignSourceMedium.php:16. Its labels are assembled directly from archived raw values in RecordBuilders/
    CampaignReporting.php:145. With CNIL active, that likely yields labels like __discarded_by_policy__ - __discarded_by_policy__ instead of the translated discard label.
  - Live.getLastVisitsDetails still appears to return the raw placeholder sentinel for marketing campaign fields. renderVisitorDetails() formats values for the HTML block in VisitorDetails.php:37,
    but extendVisitorDetails() copies raw DB values straight into the visitor payload in VisitorDetails.php:19. Core Referrers formats equivalent fields before exposing them, so this branch leaves
    API consumers with inconsistent output.

@nathangavin
Copy link
Copy Markdown
Contributor Author

@Chardonneaur Thats a good suggestion, I am unaware if allowing the end user to explicitly allow consent is permitted within the CNIL guidelines. We can address that in a followup feature.

@nathangavin
Copy link
Copy Markdown
Contributor Author

@AltamashShaikh I've fixed the formatting in a couple of places such that the translated text should now be retrieved correctly.

Copy link
Copy Markdown
Contributor

@AltamashShaikh AltamashShaikh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nathangavin

High-risk issues (blocking)

  - Columns/CampaignName.php:49 compares raw incoming campaign params against masked stored visit columns.
    Base::onNewVisit() now masks stored campaign fields under CNIL (Columns/Base.php:54, Columns/Base.php:125), but CampaignName::shouldForceNewVisit() still calls detectCampaignFromRequest() and
    compares the unmasked result to the existing visit data (Columns/CampaignName.php:71, Columns/CampaignName.php:95). With privacy masking enabled, an existing visit will hold placeholder values
    while the next request still carries raw utm_* values, so the plugin will treat the campaign as “changed” and can force a new visit repeatedly for the same campaign. This is a behavioral
    regression, not just missing coverage.

  Medium-risk issues

  - The new tests do not cover the regression above.
    tests/Integration/CorrectCampaignDetectionTest.php:176 verifies masked storage, and tests/Integration/CorrectCampaignDetectionTest.php:217 / tests/Integration/
    CorrectCampaignDetectionTest.php:242 verify formatter-style behavior, but there is no test for shouldForceNewVisit() when CNIL masking is active. That is the exact path most likely to regress
    from this change.

@nathangavin
Copy link
Copy Markdown
Contributor Author

@AltamashShaikh Fixed the force-new-visit path so that both ends of the campaign values check are normalised when trying to determine if its a new visit. Also added test coverage for this specific scenario.

Copy link
Copy Markdown
Contributor

@AltamashShaikh AltamashShaikh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also update plugin version - https://github.com/matomo-org/plugin-MarketingCampaignsReporting/blob/5.x-dev/plugin.json#L4

5.2.0, should be good and add a Changelog entry CHANGELOG.md, next release date is 2026-05-11

Also a minor fix

- The helper getCampaignValuesMaskedSettingClass() is duplicated in both MarketingCampaignsReporting.php:152 and tests/Integration/CorrectCampaignDetectionTest.php:277. That is harmless, but the
    test could call the production helper indirectly to avoid drift.
  - formatCampaignValue($value) in MarketingCampaignsReporting.php:142 is intentionally untyped. That matches current usage, but a short docblock would make the mixed input expectation clearer.

Copy link
Copy Markdown
Contributor

@AltamashShaikh AltamashShaikh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@AltamashShaikh AltamashShaikh merged commit 515d877 into 5.x-dev Apr 29, 2026
8 checks passed
@AltamashShaikh AltamashShaikh deleted the dev-20125 branch April 29, 2026 03:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants