From 730f50533db56d1bd1aa08478fe27890905490b7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 31 May 2026 23:55:45 +0000 Subject: [PATCH 1/3] Initial plan From 6939c21ab3d3fb64c7278e2851061775e2ed23cc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Jun 2026 00:09:23 +0000 Subject: [PATCH 2/3] fix: enforce premium search restrictions server-side Co-authored-by: niemyjski <1020579+niemyjski@users.noreply.github.com> --- .../Controllers/EventController.cs | 4 +++ .../Controllers/StackController.cs | 2 ++ .../Controllers/EventControllerTests.cs | 30 +++++++++++++++++++ .../Controllers/StackControllerTests.cs | 15 ++++++++++ 4 files changed, 51 insertions(+) diff --git a/src/Exceptionless.Web/Controllers/EventController.cs b/src/Exceptionless.Web/Controllers/EventController.cs index 7470abb854..7ec97ad686 100644 --- a/src/Exceptionless.Web/Controllers/EventController.cs +++ b/src/Exceptionless.Web/Controllers/EventController.cs @@ -241,6 +241,8 @@ private async Task> CountInternalAsync(AppFilter sf, T return BadRequest(far.Message); sf.UsesPremiumFeatures = pr.UsesPremiumFeatures || far.UsesPremiumFeatures; + if (sf.UsesPremiumFeatures && sf.Organizations.Any(o => !o.HasPremiumFeatures)) + return PlanLimitReached("Please upgrade your plan to use premium search features."); if (mode == "stack_new") filter = AddFirstOccurrenceFilter(ti.Range, filter); @@ -297,6 +299,8 @@ private async Task>> GetInternalAsync( return BadRequest(pr.Message); sf.UsesPremiumFeatures = pr.UsesPremiumFeatures || usesPremiumFeatures; + if (sf.UsesPremiumFeatures && sf.Organizations.Any(o => !o.HasPremiumFeatures)) + return PlanLimitReached("Please upgrade your plan to use premium search features."); try { diff --git a/src/Exceptionless.Web/Controllers/StackController.cs b/src/Exceptionless.Web/Controllers/StackController.cs index 9b9b5630c4..980a2c881f 100644 --- a/src/Exceptionless.Web/Controllers/StackController.cs +++ b/src/Exceptionless.Web/Controllers/StackController.cs @@ -489,6 +489,8 @@ private async Task>> GetInternalAsync(Ap return BadRequest(pr.Message); sf.UsesPremiumFeatures = pr.UsesPremiumFeatures; + if (sf.UsesPremiumFeatures && sf.Organizations.Any(o => !o.HasPremiumFeatures)) + return PlanLimitReached("Please upgrade your plan to use premium search features."); try { diff --git a/tests/Exceptionless.Tests/Controllers/EventControllerTests.cs b/tests/Exceptionless.Tests/Controllers/EventControllerTests.cs index 066c3d7d6c..4fc055b19a 100644 --- a/tests/Exceptionless.Tests/Controllers/EventControllerTests.cs +++ b/tests/Exceptionless.Tests/Controllers/EventControllerTests.cs @@ -519,6 +519,36 @@ public async Task CanGetFreeProjectLevelMostFrequentStackMode() Assert.Equal(2, results.Count); } + [Fact] + public async Task GetCountByProjectAsync_WithPremiumFilterOnFreeOrganization_ReturnsUpgradeRequired() + { + // Arrange + await CreateDataAsync(d => d.Event().FreeProject().Tag("premium-tag")); + + // Act & Assert + await SendRequestAsync(r => r + .AsFreeOrganizationUser() + .AppendPaths("projects", SampleDataService.FREE_PROJECT_ID, "events", "count") + .QueryString("filter", "tags:premium-tag") + .StatusCodeShouldBeUpgradeRequired() + ); + } + + [Fact] + public async Task GetByProjectAsync_WithPremiumFilterOnFreeOrganization_ReturnsUpgradeRequired() + { + // Arrange + await CreateDataAsync(d => d.Event().FreeProject().Tag("premium-tag")); + + // Act & Assert + await SendRequestAsync(r => r + .AsFreeOrganizationUser() + .AppendPaths("projects", SampleDataService.FREE_PROJECT_ID, "events") + .QueryString("filter", "tags:premium-tag") + .StatusCodeShouldBeUpgradeRequired() + ); + } + [Fact] public async Task CanGetNewStackMode() { diff --git a/tests/Exceptionless.Tests/Controllers/StackControllerTests.cs b/tests/Exceptionless.Tests/Controllers/StackControllerTests.cs index 02017715de..79b1c7ba9e 100644 --- a/tests/Exceptionless.Tests/Controllers/StackControllerTests.cs +++ b/tests/Exceptionless.Tests/Controllers/StackControllerTests.cs @@ -61,6 +61,21 @@ public async Task CanSearchByNonPremiumFields() Assert.Single(result); } + [Fact] + public async Task GetByProjectAsync_WithPremiumFilterOnFreeOrganization_ReturnsUpgradeRequired() + { + // Arrange + await CreateDataAsync(d => d.Event().FreeProject().Message("Premium restricted stack")); + + // Act & Assert + await SendRequestAsync(r => r + .AsFreeOrganizationUser() + .AppendPaths("projects", SampleDataService.FREE_PROJECT_ID, "stacks") + .QueryString("filter", "title:\"Premium restricted stack\"") + .StatusCodeShouldBeUpgradeRequired() + ); + } + [Theory] [InlineData(null)] [InlineData("1.0.0")] From 8ddad7db466039bd3b965a6101fbc98f92662baa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Jun 2026 00:13:57 +0000 Subject: [PATCH 3/3] refactor: share premium search restriction check Co-authored-by: niemyjski <1020579+niemyjski@users.noreply.github.com> --- .../Controllers/Base/ExceptionlessApiController.cs | 5 +++++ src/Exceptionless.Web/Controllers/EventController.cs | 4 ++-- src/Exceptionless.Web/Controllers/StackController.cs | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Exceptionless.Web/Controllers/Base/ExceptionlessApiController.cs b/src/Exceptionless.Web/Controllers/Base/ExceptionlessApiController.cs index 662defc7a3..813f4312d2 100644 --- a/src/Exceptionless.Web/Controllers/Base/ExceptionlessApiController.cs +++ b/src/Exceptionless.Web/Controllers/Base/ExceptionlessApiController.cs @@ -229,6 +229,11 @@ protected ObjectResult PlanLimitReached(string message) return Problem(statusCode: StatusCodes.Status426UpgradeRequired, title: message); } + protected bool IsPremiumFeatureQueryBlocked(AppFilter filter) + { + return filter.UsesPremiumFeatures && filter.Organizations.Any(o => !o.HasPremiumFeatures); + } + protected ObjectResult TooManyRequests(string message) { return Problem(statusCode: StatusCodes.Status429TooManyRequests, title: message); diff --git a/src/Exceptionless.Web/Controllers/EventController.cs b/src/Exceptionless.Web/Controllers/EventController.cs index 7ec97ad686..3d232ecc5a 100644 --- a/src/Exceptionless.Web/Controllers/EventController.cs +++ b/src/Exceptionless.Web/Controllers/EventController.cs @@ -241,7 +241,7 @@ private async Task> CountInternalAsync(AppFilter sf, T return BadRequest(far.Message); sf.UsesPremiumFeatures = pr.UsesPremiumFeatures || far.UsesPremiumFeatures; - if (sf.UsesPremiumFeatures && sf.Organizations.Any(o => !o.HasPremiumFeatures)) + if (IsPremiumFeatureQueryBlocked(sf)) return PlanLimitReached("Please upgrade your plan to use premium search features."); if (mode == "stack_new") @@ -299,7 +299,7 @@ private async Task>> GetInternalAsync( return BadRequest(pr.Message); sf.UsesPremiumFeatures = pr.UsesPremiumFeatures || usesPremiumFeatures; - if (sf.UsesPremiumFeatures && sf.Organizations.Any(o => !o.HasPremiumFeatures)) + if (IsPremiumFeatureQueryBlocked(sf)) return PlanLimitReached("Please upgrade your plan to use premium search features."); try diff --git a/src/Exceptionless.Web/Controllers/StackController.cs b/src/Exceptionless.Web/Controllers/StackController.cs index 980a2c881f..ae41cce6ac 100644 --- a/src/Exceptionless.Web/Controllers/StackController.cs +++ b/src/Exceptionless.Web/Controllers/StackController.cs @@ -489,7 +489,7 @@ private async Task>> GetInternalAsync(Ap return BadRequest(pr.Message); sf.UsesPremiumFeatures = pr.UsesPremiumFeatures; - if (sf.UsesPremiumFeatures && sf.Organizations.Any(o => !o.HasPremiumFeatures)) + if (IsPremiumFeatureQueryBlocked(sf)) return PlanLimitReached("Please upgrade your plan to use premium search features."); try