Skip to content

Remove CORS policy for production environments#588

Merged
Flaminel merged 3 commits into
mainfrom
fix_cors_policy
Apr 27, 2026
Merged

Remove CORS policy for production environments#588
Flaminel merged 3 commits into
mainfrom
fix_cors_policy

Conversation

@Flaminel

@Flaminel Flaminel commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Relates to GHSA-rwpc-36mg-fpvf

Summary by Sourcery

Restrict CORS configuration to development and remove permissive wildcard policy from production.

Enhancements:

  • Limit CORS to a named development SPA policy allowing only localhost Angular origins instead of a global wildcard policy.
  • Guard CORS middleware usage so it is only applied in development environments.

Tests:

  • Add an end-to-end test covering the previous wildcard CORS behaviour to prevent regressions related to the reported security advisory.

@Flaminel

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review

@Flaminel

Copy link
Copy Markdown
Contributor Author

@greptileai review

@Flaminel

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 27, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@sourcery-ai

sourcery-ai Bot commented Apr 27, 2026

Copy link
Copy Markdown
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Restricts CORS configuration to development environments only and tightens allowed origins for the dev SPA, ensuring production no longer uses a permissive wildcard CORS policy, with middleware updated accordingly and a new e2e test covering the previous wildcard bug.

Sequence diagram for dev vs prod CORS handling

sequenceDiagram
    actor DevUser
    actor ProdUser
    participant DevBrowser
    participant ProdBrowser
    participant Api

    DevUser->>DevBrowser: Navigate to http://localhost:4200
    DevBrowser->>Api: OPTIONS /api/resource
    activate Api
    Note over DevBrowser,Api: Origin http://localhost:4200
    Api-->>DevBrowser: 200 with Access-Control-Allow-Origin http://localhost:4200
    deactivate Api
    DevBrowser->>Api: GET /api/resource
    activate Api
    Api-->>DevBrowser: 200 with data
    deactivate Api

    ProdUser->>ProdBrowser: Navigate to production frontend
    ProdBrowser->>Api: OPTIONS /api/resource
    activate Api
    Note over ProdBrowser,Api: No CORS middleware in production
    Api-->>ProdBrowser: 200 without wildcard CORS headers
    deactivate Api
    Note over ProdBrowser: Browser blocks cross-origin call if origin is not allowed
Loading

Flow diagram for ASP.NET middleware with dev-only CORS

flowchart TD
    Start["Start Program"] --> BuildBuilder["Create WebApplicationBuilder"]
    BuildBuilder --> EnvCheck["Check builder.Environment.IsDevelopment"]

    EnvCheck -->|true| AddCors["builder.Services.AddCors"]
    AddCors --> AddPolicy["AddPolicy DevSpa with origins
http://localhost:4200,
http://127.0.0.1:4200"]
    AddPolicy --> BuildApp["Build WebApplication"]

    EnvCheck -->|false| BuildApp

    BuildApp --> ConfigureApi["ConfigureApi extension"]
    ConfigureApi --> SetupGuard["Use SetupGuardMiddleware"]
    SetupGuard --> EnvCheckApp["Check app.Environment.IsDevelopment"]

    EnvCheckApp -->|true| UseCorsDevSpa["app.UseCors DevSpa"]
    UseCorsDevSpa --> Routing["app.UseRouting"]

    EnvCheckApp -->|false| Routing

    Routing --> Auth["app.UseAuthentication"]
    Auth --> Authz["app.UseAuthorization"]
    Authz --> MapEndpoints["Map controllers and SignalR hubs"]
    MapEndpoints --> End["Run application"]
Loading

File-Level Changes

Change Details Files
Limit CORS registration to development environment and narrow dev origins for SPA access.
  • Wrap CORS service registration in a development-only environment check using builder.Environment.IsDevelopment().
  • Replace the previous permissive 'Any' CORS policy with a named 'DevSpa' policy restricted to localhost Angular dev URLs.
  • Retain credentials, headers, and methods allowances while removing the SetIsOriginAllowed wildcard behavior.
code/backend/Cleanuparr.Api/Program.cs
Apply CORS middleware only in development using the new DevSpa policy.
  • Guard UseCors with an app.Environment.IsDevelopment() check to avoid enabling CORS in production.
  • Update the CORS middleware to reference the new 'DevSpa' policy name instead of the removed 'Any' policy.
  • Ensure CORS runs in the same pipeline position (before routing/auth) but conditionally for dev only.
code/backend/Cleanuparr.Api/DependencyInjection/ApiDI.cs
Add end-to-end coverage for the CORS wildcard bug behavior.
  • Introduce a new e2e test file targeting the previous wildcard CORS misconfiguration.
  • Verify that CORS behavior aligns with the tightened configuration and that the prior vulnerability is mitigated.
e2e/tests/14-cors-wildcard-bug.spec.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • The allowed CORS origins for the dev SPA are hard-coded; consider moving them to configuration so they can be adjusted without code changes and to avoid duplicating URLs in multiple places if they change.
  • The CORS setup is now gated strictly on IsDevelopment, which will disable CORS in staging or other non-prod environments; confirm whether a broader non-production check (or environment-based configuration) would better match your deployment model.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The allowed CORS origins for the dev SPA are hard-coded; consider moving them to configuration so they can be adjusted without code changes and to avoid duplicating URLs in multiple places if they change.
- The CORS setup is now gated strictly on IsDevelopment, which will disable CORS in staging or other non-prod environments; confirm whether a broader non-production check (or environment-based configuration) would better match your deployment model.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@greptile-apps

greptile-apps Bot commented Apr 27, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes the CORS wildcard vulnerability (GHSA-rwpc-36mg-fpvf) by replacing the SetIsOriginAllowed(_ => true) policy — which allowed any origin to make credentialed cross-origin requests in all environments — with a scoped DevSpa policy restricted to http://localhost:4200 and http://127.0.0.1:4200, active only in development mode. In production the CORS middleware is no longer registered at all, which is correct since the Angular frontend is served from the same origin as the API.

Confidence Score: 4/5

Safe to merge — the security fix is correct and no regressions are introduced; one minor test robustness concern exists.

The backend changes are minimal, focused, and correct. The only finding is a P2 concern about the E2E test's unauthenticated status-code assertion, which does not affect correctness of the fix itself.

e2e/tests/14-cors-wildcard-bug.spec.ts — the 200 status assertion may fail in unauthenticated E2E runs.

Important Files Changed

Filename Overview
code/backend/Cleanuparr.Api/Program.cs Moves AddCors registration behind an IsDevelopment() guard; replaces wildcard SetIsOriginAllowed policy with a scoped DevSpa policy restricted to localhost:4200.
code/backend/Cleanuparr.Api/DependencyInjection/ApiDI.cs Wraps UseCors("DevSpa") call in an IsDevelopment() guard so the CORS middleware is never active in production.
e2e/tests/14-cors-wildcard-bug.spec.ts New Playwright regression test for GHSA-rwpc-36mg-fpvf; checks both regular and preflight requests do not reflect a malicious origin. Status-code assertion in first test may be fragile for unauthenticated runs.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Incoming Request] --> B{IsDevelopment?}
    B -- Yes --> C[UseCors DevSpa\nlocalhost:4200 only]
    B -- No --> D[No CORS middleware\nproduction - same-origin only]
    C --> E[UseRouting]
    D --> E
    E --> F[UseAuthentication]
    F --> G[UseAuthorization]
    G --> H[MapControllers / SignalR Hubs]
Loading

Reviews (1): Last reviewed commit: "added e2e tests" | Re-trigger Greptile

@codecov

codecov Bot commented Apr 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 12 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
code/backend/Cleanuparr.Api/Program.cs 0.00% 9 Missing and 1 partial ⚠️
...ackend/Cleanuparr.Api/DependencyInjection/ApiDI.cs 0.00% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Comment thread e2e/tests/14-cors-wildcard-bug.spec.ts
@coderabbitai

coderabbitai Bot commented Apr 27, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The pull request modifies CORS policy registration to remove a permissive "Any" policy and replace it with a stricter "DevSpa" policy restricted to local SPA URLs in development environments only. A regression test is added to verify the vulnerability is not reintroduced.

Changes

Cohort / File(s) Summary
CORS Policy Configuration
code/backend/Cleanuparr.Api/Program.cs, code/backend/Cleanuparr.Api/DependencyInjection/ApiDI.cs
Removed unconditional broad CORS "Any" policy registration. Implemented dev-only "DevSpa" policy restricting allowed origins to local SPA URLs while preserving header/method flexibility. Non-development environments no longer have explicit CORS configuration at this pipeline point.
CORS Security Regression Test
e2e/tests/14-cors-wildcard-bug.spec.ts
Added new Playwright e2e test suite for GHSA-rwpc-36mg-fpvf vulnerability. Tests that preflight OPTIONS and actual GET requests with malicious origin headers do not receive the attacker origin or wildcard in CORS response headers.

Sequence Diagram(s)

sequenceDiagram
    participant Attacker as Attacker<br/>(malicious origin)
    participant Client as Browser
    participant Server as Server
    
    Note over Attacker,Server: Vulnerable Configuration (Before)
    Attacker->>Client: Initiate request from attacker origin
    Client->>Server: OPTIONS preflight<br/>Origin: attacker.com
    Server-->>Client: CORS: Allow-Origin: *<br/>(permits any origin)
    Client->>Server: GET /api/auth/status<br/>Origin: attacker.com
    Server-->>Client: 200 OK<br/>CORS: Allow-Origin: *
    Note over Attacker: ❌ Vulnerability: Attacker origin allowed

    Note over Attacker,Server: Fixed Configuration (After)
    Attacker->>Client: Initiate request from attacker origin
    Client->>Server: OPTIONS preflight<br/>Origin: attacker.com
    Server-->>Client: CORS headers<br/>(restricted policy)<br/>Allow-Origin: NOT set
    Client-xAttacker: Request blocked by browser
    Note over Attacker: ✓ Fixed: Attacker origin rejected
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A CORS conundrum solved today,
Wildcard wishes whisked away,
Dev URLs tucked safe and sound,
While attackers find no hallowed ground,
Security hops with test in place! 🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main change: removing CORS policy for production environments, which is clearly reflected in the backend configuration changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description clearly relates to the changeset, referencing a security advisory and summarizing the modifications to CORS configuration and the addition of regression tests.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix_cors_policy

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
e2e/tests/14-cors-wildcard-bug.spec.ts (1)

15-17: Optionally strengthen ACAO assertions to require absence for disallowed origins.

For this attacker-origin case, asserting access-control-allow-origin is undefined would make the regression test stricter.

Also applies to: 29-31

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/tests/14-cors-wildcard-bug.spec.ts` around lines 15 - 17, The test
currently checks that the response header variable `acao` is neither
`ATTACKER_ORIGIN` nor `'*'`, which is weaker; change the assertion(s) to require
`acao` be undefined for disallowed origins by replacing the two expectations
with a single assertion that `acao` is undefined (use the existing `acao`
variable and `ATTACKER_ORIGIN` test case), and do the same for the other
occurrence of these checks later in the file so both attacker-origin cases
assert absence of the ACAO header.
code/backend/Cleanuparr.Api/Program.cs (1)

91-94: Externalize allowed dev origins instead of hardcoding them.

Consider loading the DevSpa origin list from configuration to avoid code changes when local dev topology changes.

♻️ Suggested refactor
 // CORS is needed only for development
 if (builder.Environment.IsDevelopment())
 {
+    var devSpaOrigins =
+        builder.Configuration.GetSection("Cors:DevSpaOrigins").Get<string[]>()
+        ?? new[] { "http://localhost:4200", "http://127.0.0.1:4200" };
+
     builder.Services.AddCors(options =>
     {
         options.AddPolicy("DevSpa", policy =>
         {
             policy
-                .WithOrigins("http://localhost:4200", "http://127.0.0.1:4200")
+                .WithOrigins(devSpaOrigins)
                 .AllowAnyHeader()
                 .AllowAnyMethod()
                 .AllowCredentials();
         });
     });
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/backend/Cleanuparr.Api/Program.cs` around lines 91 - 94, Replace the
hardcoded origins passed to the CORS builder
(.WithOrigins("http://localhost:4200", "http://127.0.0.1:4200")) with a list
read from configuration (e.g., a "DevSpa:AllowedOrigins" array/string in
appsettings or environment). In Program.cs obtain the IConfiguration
(builder.Configuration) and read the array (or comma-separated string) into a
string[] (with a safe fallback if null/empty), then pass that array into the
existing .WithOrigins(...) call so the same CORS chain
(.AllowAnyHeader().AllowAnyMethod().AllowCredentials()) uses configurable dev
origins. Ensure parsing handles missing config and trims values before calling
WithOrigins.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@code/backend/Cleanuparr.Api/Program.cs`:
- Around line 91-94: Replace the hardcoded origins passed to the CORS builder
(.WithOrigins("http://localhost:4200", "http://127.0.0.1:4200")) with a list
read from configuration (e.g., a "DevSpa:AllowedOrigins" array/string in
appsettings or environment). In Program.cs obtain the IConfiguration
(builder.Configuration) and read the array (or comma-separated string) into a
string[] (with a safe fallback if null/empty), then pass that array into the
existing .WithOrigins(...) call so the same CORS chain
(.AllowAnyHeader().AllowAnyMethod().AllowCredentials()) uses configurable dev
origins. Ensure parsing handles missing config and trims values before calling
WithOrigins.

In `@e2e/tests/14-cors-wildcard-bug.spec.ts`:
- Around line 15-17: The test currently checks that the response header variable
`acao` is neither `ATTACKER_ORIGIN` nor `'*'`, which is weaker; change the
assertion(s) to require `acao` be undefined for disallowed origins by replacing
the two expectations with a single assertion that `acao` is undefined (use the
existing `acao` variable and `ATTACKER_ORIGIN` test case), and do the same for
the other occurrence of these checks later in the file so both attacker-origin
cases assert absence of the ACAO header.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c76a5102-664c-4e70-892d-8c140eda8004

📥 Commits

Reviewing files that changed from the base of the PR and between 85de80a and 2d4e5e3.

📒 Files selected for processing (3)
  • code/backend/Cleanuparr.Api/DependencyInjection/ApiDI.cs
  • code/backend/Cleanuparr.Api/Program.cs
  • e2e/tests/14-cors-wildcard-bug.spec.ts

@Flaminel Flaminel merged commit 8ab4a55 into main Apr 27, 2026
8 of 11 checks passed
@Flaminel Flaminel deleted the fix_cors_policy branch April 27, 2026 10:13
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.

1 participant