Skip to content

WD-34696: Only send UTMs to Marketo for non-essential cookie consent#16216

Open
Onibenjo wants to merge 3 commits intomainfrom
WD-34696/utm-cookie-consent-check
Open

WD-34696: Only send UTMs to Marketo for non-essential cookie consent#16216
Onibenjo wants to merge 3 commits intomainfrom
WD-34696/utm-cookie-consent-check

Conversation

@Onibenjo
Copy link
Copy Markdown
Contributor

@Onibenjo Onibenjo commented Apr 8, 2026

Done

  • UTM parameters are now only attached to the Marketo enrichment payload when the user has consented to non-essential cookies (functionality, performance, or all)
  • When consent is essential or unset (or cookie is missing), UTMs are stripped from the payload before it reaches Marketo
  • Includes a temporary ?dry_run=true query param on /marketo/submit for QA — returns the payloads as JSON without calling the Marketo API. Remove before merge.

QA

This can be tested entirely on the DEMO using curl. The ?dry_run=true param returns the Marketo payloads as JSON without making any API calls.

Test 1: essential consent — UTMs should NOT be sent

curl -s -X POST "https://ubuntu-com-16202.demos.haus/marketo/submit?dry_run=true" -b "_cookies_accepted=essential; utms=utm_source%3Agoogle%26utm_medium%3Acpc" -d "formid=4198&email=test@test.com" | python3 -m json.tool

Expected: non_essential_consent: false, and enriched_payload.input[0].leadFormFields should have no utm_source or utm_medium keys.

Test 2: performance consent — UTMs SHOULD be sent

curl -s -X POST "https://ubuntu-com-16202.demos.haus/marketo/submit?dry_run=true" -b "_cookies_accepted=performance; utms=utm_source%3Agoogle%26utm_medium%3Acpc" -d "formid=4198&email=test@test.com" | python3 -m json.tool

Expected: non_essential_consent: true, and enriched_payload.input[0].leadFormFields should contain utm_source: "google" and utm_medium: "cpc".

Test 3: functionality consent — UTMs SHOULD be sent

curl -s -X POST "https://ubuntu-com-16202.demos.haus/marketo/submit?dry_run=true" -b "_cookies_accepted=functionality; utms=utm_source%3Agoogle%26utm_medium%3Acpc" -d "formid=4198&email=test@test.com" | python3 -m json.tool

Expected: non_essential_consent: true, UTM fields present in enriched payload.

Test 4: all consent — UTMs SHOULD be sent

curl -s -X POST "https://ubuntu-com-16202.demos.haus/marketo/submit?dry_run=true" \
  -b "_cookies_accepted=all; utms=utm_source%3Agoogle%26utm_medium%3Acpc" \
  -d "formid=4198&email=test@test.com" | python3 -m json.tool

Expected: non_essential_consent: true, UTM fields present in enriched payload.

Test 5: No _cookies_accepted cookie — UTMs should NOT be sent

curl -s -X POST "https://ubuntu-com-16202.demos.haus/marketo/submit?dry_run=true" \
  -b "utms=utm_source%3Agoogle%26utm_medium%3Acpc" \
  -d "formid=4198&email=test@test.com" | python3 -m json.tool

Expected: cookie_consent: "unset", non_essential_consent: false, no UTM fields in enriched payload.

Summary table

_cookies_accepted UTMs in enriched payload?
essential No
unset / missing No
functionality Yes
performance Yes
all Yes

Before merge

  • Remove the dry_run block from webapp/views.py

Issue / Card

Fixes https://warthogs.atlassian.net/browse/WD-34696

Help

QA steps - Commit guidelines

Check the _cookies_accepted cookie in marketo_submit() and only attach
UTM values to the enrichment payload when the consent type is
functionality, performance, or all. Essential and unset consent types
will no longer have UTM data forwarded to Marketo.

Includes a temporary dry_run query param for QA (to be removed before merge).
Copilot AI review requested due to automatic review settings April 8, 2026 10:01
@webteam-app
Copy link
Copy Markdown

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR aims to ensure UTM parameters are only sent to Marketo when the user has consented to non-essential cookies, aligning Marketo enrichment behavior with cookie consent state.

Changes:

  • Added consent detection via _cookies_accepted and gated attaching UTM fields to the enrichment payload.
  • Prevented parsing/attaching UTMs from the encoded utms cookie/form field when consent is essential/unset.
  • Added a temporary dry_run=true mode on /marketo/submit that returns payloads as JSON (intended for QA only).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

enrichment_fields["acquisition_url"] = enriched_acquisition_url
if non_essential_consent:
utms = unquote(encoded_utms)
utm_dict = dict(i.split(":", 1) for i in utms.split("&"))
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

utm_dict = dict(i.split(":", 1) for i in utms.split("&")) will raise ValueError if any segment lacks a : (e.g., the JS cookie builder emits just the key when a UTM value is empty). Consider parsing defensively by skipping malformed segments or treating key-only segments as an empty value so a single bad entry can’t 500 the request.

Suggested change
utm_dict = dict(i.split(":", 1) for i in utms.split("&"))
utm_dict = {}
for utm_segment in utms.split("&"):
if not utm_segment:
continue
if ":" in utm_segment:
key, value = utm_segment.split(":", 1)
else:
key, value = utm_segment, ""
if key:
utm_dict[key] = value

Copilot uses AI. Check for mistakes.
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.

Left as is.
The current change did not change this

webapp/views.py Outdated
Comment on lines +1167 to +1174
# TODO: Remove after QA - returns payloads without hitting Marketo
if flask.request.args.get("dry_run") == "true":
return flask.jsonify({
"dry_run": True,
"cookie_consent": cookie_consent,
"non_essential_consent": non_essential_consent,
"payload": payload,
"enriched_payload": enriched_payload,
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

The dry_run=true path returns full payload/enriched_payload JSON including PII (e.g., email, visitor data) to any caller. This should not ship to production; please remove before merge, or at minimum gate it behind a non-production environment check and avoid echoing sensitive fields.

Suggested change
# TODO: Remove after QA - returns payloads without hitting Marketo
if flask.request.args.get("dry_run") == "true":
return flask.jsonify({
"dry_run": True,
"cookie_consent": cookie_consent,
"non_essential_consent": non_essential_consent,
"payload": payload,
"enriched_payload": enriched_payload,
# QA-only dry-run mode: do not expose raw payload data or allow in production.
if flask.request.args.get("dry_run") == "true":
app_env = (
os.environ.get("APP_ENV")
or os.environ.get("FLASK_ENV")
or os.environ.get("ENV")
or ""
).lower()
if app_env == "production":
return (
flask.jsonify({"error": "dry_run is not available in production."}),
403,
)
return flask.jsonify({
"dry_run": True,
"cookie_consent": cookie_consent,
"non_essential_consent": non_essential_consent,
"payload_summary": {
"formId": payload.get("formId"),
"input_count": len(payload.get("input", [])),
},
"enriched_payload_summary": {
"formId": enriched_payload.get("formId"),
"input_count": len(enriched_payload.get("input", [])),
"leadFormFieldNames": sorted(enrichment_fields.keys()),
},

Copilot uses AI. Check for mistakes.
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.

This will be removed after QA

Comment on lines +1127 to +1132
# Only attach UTM values when the user has consented to
# non-essential cookies (functionality, performance, or all)
cookie_consent = flask.request.cookies.get("_cookies_accepted", "unset")
non_essential_consent = cookie_consent in {
"functionality", "performance", "all"}

Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

This change introduces new consent-dependent behavior for what gets sent to Marketo, but there are no unit tests covering the /marketo/submit path. Since webapp/views.py already has view-level tests for related UTM helpers, add tests that assert UTMs are stripped when consent is essential/missing and preserved when consent is functionality/performance/all (including UTMs provided via hidden fields and via acquisition_url).

Copilot uses AI. Check for mistakes.
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 8, 2026

Codecov Report

❌ Patch coverage is 0% with 33 lines in your changes missing coverage. Please review.
✅ Project coverage is 48.37%. Comparing base (32502aa) to head (a102b20).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
webapp/views.py 0.00% 33 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #16216      +/-   ##
==========================================
- Coverage   48.52%   48.37%   -0.15%     
==========================================
  Files          37       37              
  Lines        5875     5893      +18     
==========================================
  Hits         2851     2851              
- Misses       3024     3042      +18     
Files with missing lines Coverage Δ
webapp/views.py 46.47% <0.00%> (-0.89%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment on lines +1201 to +1211
# TODO: Remove after QA - returns payloads without hitting Marketo
if flask.request.args.get("dry_run") == "true":
return flask.jsonify(
{
"dry_run": True,
"cookie_consent": cookie_consent,
"non_essential_consent": non_essential_consent,
"payload": payload,
"enriched_payload": enriched_payload,
}
)
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.

Will remove after QA

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.

3 participants