Skip to content

fix(experiments): support /cmsHomePage vanity for experiment URL matching#35360

Merged
dsolistorres merged 13 commits intomainfrom
fix/issue-34747-cmshomepage-experiments
Apr 21, 2026
Merged

fix(experiments): support /cmsHomePage vanity for experiment URL matching#35360
dsolistorres merged 13 commits intomainfrom
fix/issue-34747-cmshomepage-experiments

Conversation

@dsolistorres
Copy link
Copy Markdown
Member

Summary

Fixes the remaining scope of #34747 — Experiments not firing when the experiment page is reached via the legacy /cmsHomePage vanity URL fallback.

  • VanityUrlAPIImpl.resolveVanityUrl has a legacy fallback (dotCMS/src/main/java/com/dotcms/vanityurl/business/VanityUrlAPIImpl.java:257-261) that, when a visitor requests / and no vanity directly matches, resolves /cmsHomePage instead and forwards (action=200) to the configured target page. The browser URL bar stays at /.
  • ExperimentUrlPatternCalculator.getVanityUrlsRegex was only adding the vanity's own URI (/cmsHomePage) as a regex alternative — so the incoming analytics URL http://site/ (or http://site) never matched and the Experiment engine always served the Original variant.
  • This PR adds a /? alternative (matching / and empty) to the generated regex when any returned vanity has URI /cmsHomePage. The approach mirrors the existing permissive pattern used in RootIndexRegexUrlPatterStrategy for the real root-index page.

An earlier PR (#26803, commit 53718e0) fixed the common vanity-URL case but did not cover the /cmsHomePage legacy fallback — details in the issue comment thread.

Also removes an unused CMS_HOME_PAGE constant from VisitorFilter.

Test plan

  • New integration test ExperimentUrlPatternCalculatorIntegrationTest#experimentWithCmsHomePageVanity — fails on main, passes on this branch.
  • Regression run of the four pre-existing vanity tests in the same class — all still pass (Tests run: 5, Failures: 0, Errors: 0).
  • QA: see "Notes for QA" on the issue — confirm fix for the /cmsHomePage scenario and regression-check other vanity-URL + Experiment combinations.

Refs: #34747

🤖 Generated with Claude Code

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 16, 2026

Claude finished @dsolistorres's task in 4m 8s —— View job


🔍 dotCMS Backend Review

  • Load dotCMS conventions
  • Get the Java diff
  • Run security, database, Java standards, and REST API sub-agents in parallel
  • Consolidate findings and post review

Review posted to comment #4277243445. Found 5 medium-severity findings across security, database, and Java standards domains. REST API review was clean (NO_FINDINGS).

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 16, 2026

🔍 dotCMS Backend Review

[🟡 Medium] dotCMS/src/main/java/com/dotcms/experiments/business/ExperimentUrlPatternCalculator.java:122

When CachedVanityUrl.normalize() returns "" for an invalid URI, vanity.pattern.pattern() returns "". That empty string is embedded verbatim into DEFAULT_URL_REGEX_TEMPLATE, producing a path alternative that matches any URL — not just the experiment's page. The assembled catch-all regex is then serialized into the Analytics SDK payload, which could cause the experiment to record page-view events for every URL the visitor navigates to. This is pre-existing in the prior .pattern toString path as well, but the explicit .pattern() call makes it easy to guard here.

// line 122 — empty pattern source yields a catch-all template expansion
.map(vanity -> String.format(DEFAULT_URL_REGEX_TEMPLATE, vanity.pattern.pattern()))

💡 Add a guard to skip blank pattern sources:

vanityUrls.stream()
    .filter(vanity -> !vanity.pattern.pattern().isEmpty())
    .map(vanity -> String.format(DEFAULT_URL_REGEX_TEMPLATE, vanity.pattern.pattern()))

Next steps

  • 🟡 You can ask me to handle mechanical fixes inline: @claude fix <issue description> in <File.java>
  • Every new push triggers a fresh review automatically

@dsolistorres dsolistorres self-assigned this Apr 16, 2026
@dsolistorres dsolistorres force-pushed the fix/issue-34747-cmshomepage-experiments branch from a7a3d5a to 52fbcf2 Compare April 16, 2026 20:25
@dsolistorres dsolistorres linked an issue Apr 16, 2026 that may be closed by this pull request
@dsolistorres dsolistorres enabled auto-merge April 16, 2026 21:11
@dsolistorres dsolistorres force-pushed the fix/issue-34747-cmshomepage-experiments branch from 1eff62f to bf98f5a Compare April 17, 2026 14:33
@dsolistorres dsolistorres force-pushed the fix/issue-34747-cmshomepage-experiments branch from ce3c444 to bd5e5a4 Compare April 17, 2026 21:17
@dsolistorres
Copy link
Copy Markdown
Member Author

Tracked the client-side ReDoS hardening discussion as a follow-up: #35379.

Surfaced during this review but scoped out of the PR — it's a cross-cutting SDK hardening decision (Worker + timeout, static regex linting, re2-wasm, or input-length cap) that deserves its own design review rather than being bolted onto the /cmsHomePage fix.

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 19, 2026

Pull Request Unsafe to Rollback!!!

  • Category: M-4 — OSGi Public Interface / Service Change
  • Risk Level: 🟡 MEDIUM
  • Why it's unsafe: VanityUrlAPI.findByForward is a public interface method whose signature changed from 4 parameters to 5 parameters (new boolean includeSystemHost appended). The old 4-argument overload is gone — not deprecated, not kept as a default. Any OSGi plugin that (a) calls APILocator.getVanityUrlAPI().findByForward(host, language, forward, 200) will get NoSuchMethodError at runtime on N, or (b) implements VanityUrlAPI will fail activation with AbstractMethodError because the new method is not implemented. For rollback: if an operator has updated a plugin to use the new 5-arg signature compiled against N, rolling back the dotCMS JAR to N-1 immediately breaks that plugin with NoSuchMethodError.
  • Code that makes it unsafe: dotCMS/src/main/java/com/dotcms/vanityurl/business/VanityUrlAPI.java — old declaration List<CachedVanityUrl> findByForward(Host host, Language language, String forward, int action) is replaced (not overloaded) by List<CachedVanityUrl> findByForward(Host host, Language language, String forward, int action, boolean includeSystemHost).
  • Alternative (if possible): Add the 5-arg overload as a new default interface method and keep the old 4-arg signature as a deprecated default that delegates to the new one with includeSystemHost = false. This preserves binary compatibility for all existing callers and implementors while surfacing the new behavior. Example: default List<CachedVanityUrl> findByForward(Host h, Language l, String f, int a) { return findByForward(h, l, f, a, false); }

  • Category: M-4 — OSGi Public Interface / Service Change (constant removal from Impl)
  • Risk Level: 🟡 MEDIUM
  • Why it's unsafe: VanityUrlAPIImpl.LEGACY_CMS_HOME_PAGE was a public static final field that was removed from the Impl class and moved to the VanityUrlAPI interface. Java does not make static members of interfaces accessible via implementing classes at the binary level, so any OSGi plugin that imported com.dotcms.vanityurl.business.VanityUrlAPIImpl.LEGACY_CMS_HOME_PAGE will get NoSuchFieldError on N. After rollback to N-1 such a plugin works again — but the N-to-N-1 rollback window is the risk.
  • Code that makes it unsafe: dotCMS/src/main/java/com/dotcms/vanityurl/business/VanityUrlAPIImpl.java line removed: public static final String LEGACY_CMS_HOME_PAGE = "/cmsHomePage";. Confirmed consumer migration in dotcms-integration/src/test/java/com/dotmarketing/filters/FiltersTest.java: import changed from VanityUrlAPIImpl.LEGACY_CMS_HOME_PAGE to VanityUrlAPI.LEGACY_CMS_HOME_PAGE.
  • Alternative (if possible): Keep VanityUrlAPIImpl.LEGACY_CMS_HOME_PAGE as a @Deprecated forwarding constant pointing at the new interface constant (public static final String LEGACY_CMS_HOME_PAGE = VanityUrlAPI.LEGACY_CMS_HOME_PAGE;) for one release cycle, then remove it.

@dsolistorres dsolistorres force-pushed the fix/issue-34747-cmshomepage-experiments branch from 733f154 to 122e1ad Compare April 19, 2026 22:28
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 20, 2026

Pull Request Unsafe to Rollback!!!

  • Category: M-4 — OSGi Public Interface / Service Change (residual — partially mitigated since prior analysis)
  • Risk Level: 🟡 MEDIUM
  • Why it's unsafe: VanityUrlAPI now declares findByForward(Host, Language, String, int, boolean) as a new abstract method. Any OSGi plugin that previously implemented VanityUrlAPI (as an alternative provider registered with APILocator) implemented only the old 4-arg abstract form. On N, that plugin's class has no implementation of the 5-arg abstract method. When ExperimentUrlPatternCalculator calls APILocator.getVanityUrlAPI().findByForward(host, language, uri, 200, true), the JVM dispatches to the registered provider; if that provider is a plugin instance lacking the 5-arg method, it throws AbstractMethodError.
    • What is now safe (improvements since prior analysis): The old 4-arg overload is preserved as a default method — so OSGi plugins that only call findByForward(4-arg) are no longer affected. Additionally, VanityUrlAPIImpl.LEGACY_CMS_HOME_PAGE is retained as a forwarding alias (= VanityUrlAPI.LEGACY_CMS_HOME_PAGE), so plugins referencing the field on the Impl class still compile and run correctly.
    • What remains unsafe: The 5-arg method is declared abstract (not default). A plugin that implements VanityUrlAPI — i.e., provides an alternative provider registered via the OSGi service registry — does not implement findByForward(5-arg). Any invocation of the 5-arg method on that plugin instance raises AbstractMethodError.
  • Code that makes it unsafe: dotCMS/src/main/java/com/dotcms/vanityurl/business/VanityUrlAPI.java lines 142–143 — List<CachedVanityUrl> findByForward(Host host, Language language, String forward, int action, boolean includeSystemHost); is abstract with no default body. The only call site is ExperimentUrlPatternCalculator.getVanityUrlsRegex() (line 112), which reaches this through APILocator.getVanityUrlAPI().
  • Alternative (if possible): Give the 5-arg method a default body that delegates to the host-only load path (matching the pre-PR behavior), so plugins that do not override it get a safe, backward-compatible implementation automatically. Example:
    default List<CachedVanityUrl> findByForward(Host host, Language language,
                                                 String forward, int action,
                                                 boolean includeSystemHost) {
        // default: host-only; override to include SYSTEM_HOST
        return findByForward(host, language, forward, action);
    }
    Then have VanityUrlAPIImpl override this default with the SYSTEM_HOST-aware logic. This makes the 5-arg a non-breaking addition for all existing implementors.

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 20, 2026

🔍 dotCMS Backend Review

[🟡 Medium] dotCMS/src/main/java/com/dotcms/vanityurl/business/VanityUrlAPIImpl.java:484

cachedVanityUrl.forwardTo.equals(forward) calls .equals on the field, not the parameter. If any CachedVanityUrl in the cache has a null forwardTo (corrupted or partially-constructed entry), this throws NullPointerException inside the stream and aborts experiment URL pattern calculation. CachedVanityUrl's own equals/hashCode already guard against null forwardTo, indicating the authors know it can be null.

.filter(cachedVanityUrl -> cachedVanityUrl.forwardTo.equals(forward))

💡 Flip the receiver: forward.equals(cachedVanityUrl.forwardTo), or use Objects.equals(cachedVanityUrl.forwardTo, forward).


[🟡 Medium] dotCMS/src/main/java/com/dotcms/vanityurl/business/VanityUrlAPIImpl.java:462-469

The 4-arg findByForward delegates to the 5-arg overload via a direct this.findByForward(...) self-call. The inline comment asserts "ByteBuddy advice fires on the self-invocation" — this is factually incorrect. ByteBuddy/CDI proxy advice only intercepts calls arriving through the proxy, not this. direct invocations within the same class. As a result, the @CloseDBIfOpened annotation on the 5-arg method does not fire when the 4-arg overload is the external entry point, leaving any DB connection opened by the load()findInDb() cold-cache path potentially unclosed.

// "ByteBuddy advice fires on the self-invocation" ← factually incorrect
return findByForward(host, language, forward, action, false);

💡 Remove the incorrect comment. Add @CloseDBIfOpened directly to the 4-arg overload so the annotation fires correctly when it is the proxy-dispatched entry point.


[🟡 Medium] dotCMS/src/main/java/com/dotcms/vanityurl/business/VanityUrlAPIImpl.java:70

VanityUrlAPIImpl.LEGACY_CMS_HOME_PAGE is retained as a forwarding alias for source compatibility, and the Javadoc correctly says new code should use VanityUrlAPI.LEGACY_CMS_HOME_PAGE. However, the field is missing @Deprecated, so IDEs and compilers will not warn callers who reference the old location — defeating the migration signal the compat alias is supposed to provide.

public static final String   LEGACY_CMS_HOME_PAGE = VanityUrlAPI.LEGACY_CMS_HOME_PAGE;

💡 Add @Deprecated to the field so callers see the IDE warning to migrate to VanityUrlAPI.LEGACY_CMS_HOME_PAGE.


[🟡 Medium] dotCMS/src/main/java/com/dotcms/vanityurl/business/VanityUrlAPI.java:142-147

findByForward(..., includeSystemHost=true) performs no permission check before broadening the result to include SYSTEM_HOST vanities. The restriction to system-user / internal contexts is documented only in Javadoc, with no enforcement at the API boundary. Any future caller in a request-scoped path (REST endpoint, rendering pipeline) that passes includeSystemHost=true will silently receive SYSTEM_HOST vanity forwardTo paths and URI patterns for a host the caller may not be authorized to inspect.

default List<CachedVanityUrl> findByForward(..., final boolean includeSystemHost) {
    return findByForward(host, language, forward, action); // no permission guard
}

💡 Consider moving the SYSTEM_HOST-broadened query to a package-private helper in VanityUrlAPIImpl so it is not accessible through the public interface flag.


[🟡 Medium] dotCMS/src/main/java/com/dotcms/experiments/business/ExperimentUrlPatternCalculator.java:127

vanity.pattern.pattern() embeds raw admin-authored regex source verbatim into the Analytics SDK payload, which is evaluated client-side via new RegExp(...).test(...). VanityUrlUtil.isValidRegex verifies Java syntax validity but does not bound complexity. A vanity URI with nested quantifiers (e.g. /(a+)+$) that passes Java validation is a ReDoS pattern in browser JS engines. The code's own Javadoc acknowledges this and links to follow-up #35379; surfaced here because the assembled pattern ships to every visitor's browser for any page under an active experiment.

.map(vanity -> String.format(DEFAULT_URL_REGEX_TEMPLATE, vanity.pattern.pattern()))

💡 A server-side complexity heuristic (e.g. reject patterns containing (x+)+, (x*)*) at assembly time would eliminate the client-side DoS vector before #35379 ships.


Next steps

  • 🟡 You can ask me to handle mechanical fixes inline: @claude fix <issue description> in <File.java>
  • Every new push triggers a fresh review automatically

@dsolistorres dsolistorres force-pushed the fix/issue-34747-cmshomepage-experiments branch from 0694bb5 to 27afec3 Compare April 20, 2026 16:46
dsolistorres and others added 7 commits April 20, 2026 16:52
…hing (#34747)

VanityUrlAPIImpl.resolveVanityUrl has a legacy fallback that forwards a "/"
request to the /cmsHomePage vanity. ExperimentUrlPatternCalculator did not
account for this, so the generated regex only matched /cmsHomePage (not /)
and variants were never served when the experiment page was reached via that
fallback.

Adds an integration test covering the /cmsHomePage scenario and removes an
unused /cmsHomePage constant from VisitorFilter.

Refs: #34747

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…face

Addresses PR review feedback: the experiments package was reaching into
VanityUrlAPIImpl for the LEGACY_CMS_HOME_PAGE constant, leaking an impl
detail across module boundaries. Moves the constant to the VanityUrlAPI
interface (matching the existing VANITY_URL_RESPONSE_HEADER pattern) and
updates callers.

Refs: #34747

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses PR review feedback: passing a java.util.regex.Pattern to
String.format("%s", ...) relies on implicit Pattern.toString(). Switch to
vanity.pattern.pattern() so the intent (extract the regex source string) is
explicit. Semantically a no-op.

Refs: #34747

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses PR review feedback: streams are single-use and lazy; storing them
in named locals invites misuse. Inline both streams directly into
Stream.concat(...) since each is consumed exactly once. Semantically a no-op.

Refs: #34747

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
findByForward previously only searched the given host, unlike resolveVanityUrl
which falls back to SYSTEM_HOST. As a result, a /cmsHomePage vanity published
on SYSTEM_HOST (the recommended setup for cross-site home-page aliasing) would
not be picked up by the Experiment URL regex calculator, and experiments on
the target page would still be skipped for visitors arriving at "/".

Updates findByForward to also collect vanities from SYSTEM_HOST, mirroring
resolveVanityUrl's host resolution. Adds an integration test covering the
SYSTEM_HOST + /cmsHomePage scenario.

Refs: #34747

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CachedVanityUrl compiles each vanity's URI with Pattern.CASE_INSENSITIVE,
so VanityUrlAPIImpl.resolveVanityUrl triggers the /cmsHomePage legacy
fallback even when an admin created the vanity with a different casing
(/cmshomepage, /CmsHomePage, etc.). The previous equals() check missed
those variants and the "/" regex fallback would not be added.

Refs: #34747

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ching

The server-assembled vanity-URL regex preserved the admin-entered casing of
each vanity URI, but the SDK tracker (parser.ts verifyRegex) lowercases the
incoming URL path before matching. A mixed-case vanity URI such as
/cmsHomePage therefore never matched the lowercased client path, even though
server-side vanity resolution itself is case-insensitive (CachedVanityUrl
compiles patterns with Pattern.CASE_INSENSITIVE).

Mirrors the toLowerCase() already applied to the experiment page regex in
calculatePageUrlRegexPattern. Tests updated to use lowercase URLs that
reflect real SDK matching.

Refs: #34747

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dsolistorres and others added 6 commits April 20, 2026 16:52
…tract, API contract

- ExperimentUrlPatternCalculator.calculatePageUrlRegexPattern: Javadoc note
  that the returned regex is consumed client-side by the Analytics SDK and is
  NOT protected by MatcherTimeoutFactory; follow-up tracked in #35379.
- ExperimentUrlPatternCalculator.getVanityUrlsRegex: extract the
  /cmsHomePage-detection anyMatch into a named boolean so vanityUrls is no
  longer traversed twice inside Stream.concat. Adds a comment clarifying that
  the exact-match check is intentional — regex-based cmsHomePage URIs are
  unsupported by design, mirroring resolveVanityUrl's literal fallback lookup.
- VanityUrlAPI.findByForward: Javadoc note that this is intended for
  system-user / internal routing contexts and performs no permission check;
  callers without READ permission on the host, or paths that expose results
  to end users, must not use it.

Refs: #34747

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The final toLowerCase() in getVanityUrlsRegex affects every vanity URL
alternative in the assembled pattern, not only the /cmsHomePage one. A
reviewer flagged that this is a scope expansion: admin-authored vanity URIs
that rely on uppercase characters or uppercase-only character classes
(e.g. "[A-Z]+") lose those semantics in this path.

Document the behavior and its justification in both the public Javadoc on
calculatePageUrlRegexPattern and in the inline comment at the return
statement. Rationale: CachedVanityUrl already compiles each vanity's URI
with Pattern.CASE_INSENSITIVE, so case-sensitive regex constructs never
actually influence vanity matching anywhere — no consumer loses behavior
by the lowercase fold here.

Refs: #34747

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…licit

Previous iteration silently widened findByForward to also return SYSTEM_HOST
vanities, documented only in Javadoc. Caller intent is now visible at every
call site via a required includeSystemHost boolean parameter:

- VanityUrlAPI.findByForward: new 5-arg signature; Javadoc updated.
- VanityUrlAPIImpl.findByForward: flag gates SYSTEM_HOST stream inclusion.
- ExperimentUrlPatternCalculator.getVanityUrlsRegex: passes true with a comment
  explaining why (a /cmsHomePage vanity forwarding to the experiment page may
  live on SYSTEM_HOST).
- VanityUrlAPITest: three direct findByForward tests pass false — they assert
  host-specific behavior (pre-PR semantics preserved).

Regression suite: 6 experiment + 3 findByForward tests all pass.

Refs: #34747

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…/ LEGACY_CMS_HOME_PAGE

The previous iteration changed the public `findByForward` signature to require
an explicit `includeSystemHost` flag, and moved `LEGACY_CMS_HOME_PAGE` from
`VanityUrlAPIImpl` to the `VanityUrlAPI` interface. Both were source-breaking
changes for external consumers.

- VanityUrlAPI: add a default 4-arg `findByForward` overload that delegates
  to the 5-arg form with `includeSystemHost = false`, restoring pre-PR
  behavior for callers that relied on host-specific results.
- VanityUrlAPIImpl: re-add `LEGACY_CMS_HOME_PAGE` as a public-static-final
  shim pointing at the canonical interface constant; update the internal
  `resolveVanityUrl` reference to qualify as `VanityUrlAPI.LEGACY_CMS_HOME_PAGE`
  so the impl's own code doesn't depend on the compat field.

Refs: #34747

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…d regex

CachedVanityUrl.normalize() returns "" when VanityUrlUtil.isValidRegex
rejects the URI as an invalid regex, in which case the compiled Pattern's
source is also "". Previously such vanities were still joined into the
assembled vanity URL regex; String.format on the URL template then produced
a path alternative that matched any URL, effectively creating a catch-all
for the experiment and causing page-view events to be recorded for every
URL the visitor navigated to.

Filter out those entries before the map step so only vanities with a
non-empty pattern source contribute alternatives.

Refs: #34747

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous iteration made the new 5-arg findByForward(..., boolean includeSystemHost)
abstract on the VanityUrlAPI interface, so any OSGi alternative provider that
only implemented the pre-PR 4-arg abstract method would throw AbstractMethodError
when the 5-arg is invoked on its instance.

Invert the abstract/default split on the interface so the 5-arg is the default
method and the 4-arg stays abstract with pre-PR semantics:

- 4-arg findByForward is now the canonical abstract method (pre-PR shape).
  All existing implementors already override it, so nothing breaks.
- 5-arg findByForward gets a default body that delegates to the 4-arg
  (host-only results). Legacy providers that don't override the 5-arg get a
  safe, backward-compatible implementation automatically.
- VanityUrlAPIImpl overrides both: the 4-arg delegates to the 5-arg with
  false; the 5-arg carries @CloseDBIfOpened and the SYSTEM_HOST-aware logic.
  ByteBuddy bytecode advice fires on the self-invocation, so the 4-arg
  delegation preserves connection lifecycle without duplicating the
  annotation.

Regression suite: 6 experiment + 3 findByForward tests all pass.

Refs: #34747

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dsolistorres dsolistorres force-pushed the fix/issue-34747-cmshomepage-experiments branch from 27afec3 to 0b9d0ee Compare April 20, 2026 22:52
@dsolistorres dsolistorres added this pull request to the merge queue Apr 21, 2026
Merged via the queue into main with commit b7f9581 Apr 21, 2026
49 checks passed
@dsolistorres dsolistorres deleted the fix/issue-34747-cmshomepage-experiments branch April 21, 2026 13:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI: Safe To Rollback Area : Backend PR changes Java/Maven backend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[DEFECT] Experiments not firing when using VanityURLs

2 participants