Skip to content

security: harden production settings for GraphQL, HSTS, and HTTP headers (fixes #3783)#3799

Merged
arkid15r merged 18 commits intoOWASP:mainfrom
ScienHAC:main
Feb 12, 2026
Merged

security: harden production settings for GraphQL, HSTS, and HTTP headers (fixes #3783)#3799
arkid15r merged 18 commits intoOWASP:mainfrom
ScienHAC:main

Conversation

@ScienHAC
Copy link
Contributor

@ScienHAC ScienHAC commented Feb 6, 2026

Proposed change

Resolves #3783

This PR implements several security hardening improvements aligned with OWASP recommendations to strengthen the application’s production security posture.

Summary of changes

  • GraphQL Introspection

    • Disabled GraphQL introspection when DEBUG = False in backend/settings/graphql.py
    • Prevents unintended exposure of the API schema in production environments
  • HTTPS & HSTS Enforcement

    • Enabled SECURE_SSL_REDIRECT, SECURE_HSTS_SECONDS, SECURE_HSTS_INCLUDE_SUBDOMAINS, and SECURE_HSTS_PRELOAD in backend/settings/production.py
    • Ensures encrypted connections and mitigates downgrade and man-in-the-middle attacks
  • MIME Sniffing Protection

    • Set SECURE_CONTENT_TYPE_NOSNIFF = True in backend/settings/base.py
    • Adds the X-Content-Type-Options: nosniff header to prevent content-type sniffing attacks

These changes improve security without affecting existing functionality and bring the project closer to OWASP best practices.

Checklist

  • Required: I followed the contributing workflow
  • Required: I verified that my code works as intended and resolves the issue as described
  • Required: I ran make check-test locally: all warnings addressed, tests passed
  • I used AI for code, documentation, tests, or communication related to this PR

Copilot AI review requested due to automatic review settings February 6, 2026 05:31
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 6, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Summary by CodeRabbit

  • Chores
    • Strengthened production security (HSTS, content-type sniffing, secure cookies, SSL redirect, proxy SSL header)
    • Added environment-specific security overrides for local, test, E2E, and fuzz setups (relaxed cookie, HSTS and SSL settings)
    • Updated GraphQL behavior to disable introspection in non-debug deployments and enforce query depth limits

Walkthrough

Adds several Django security settings to base and environment configs, conditionally disables GraphQL introspection in non-DEBUG/non-FUZZ production, and relaxes those security settings in local/e2e/fuzz/test environments; staging only has whitespace cleanup.

Changes

Cohort / File(s) Summary
Base Security Settings
backend/settings/base.py
Adds production-facing security flags: CSRF_COOKIE_SECURE, SECURE_CONTENT_TYPE_NOSNIFF, SECURE_HSTS_INCLUDE_SUBDOMAINS, SECURE_HSTS_PRELOAD, SECURE_HSTS_SECONDS, SECURE_PROXY_SSL_HEADER, and SECURE_SSL_REDIRECT.
GraphQL Schema / Introspection
backend/settings/graphql.py
Refactors schema creation to build an extensions list and conditionally appends DisableIntrospection when not DEBUG and not in FUZZ, and uses strawberry.Schema(extensions=..., query=..., mutation=...).
Non‑production Overrides (local / e2e / fuzz / test)
backend/settings/local.py, backend/settings/e2e.py, backend/settings/fuzz.py, backend/settings/test.py
Adds environment-specific overrides that disable or relax security flags for development/testing: CSRF_COOKIE_SECURE = False, SECURE_HSTS_SECONDS = 0, SECURE_PROXY_SSL_HEADER = None, SECURE_SSL_REDIRECT = False, and SESSION_COOKIE_SECURE = False (where applicable).
Formatting / Whitespace
backend/settings/staging.py
Whitespace cleanup only — removed extra blank lines; no functional changes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The changeset implements most objectives from #3783 but contains a critical discrepancy: security settings are applied to multiple environments (base, local, test, e2e, fuzz) rather than only production.py as specified. Verify whether disabling SSL/HSTS in non-production environments is intentional. If not, consolidate security settings to production.py only and ensure e2e/test environments properly override base defaults.
Out of Scope Changes check ⚠️ Warning The PR includes out-of-scope changes: security settings added to local.py, test.py, e2e.py, and fuzz.py environments were not mentioned in issue #3783, which specified only production.py modifications. Remove environment-specific security overrides from local.py, test.py, e2e.py, and fuzz.py unless they were explicitly requested in the issue or are necessary for test isolation.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: security hardening for production settings including GraphQL introspection, HSTS, and HTTP headers.
Description check ✅ Passed The description is directly related to the changeset, detailing specific security hardening changes including GraphQL introspection disabling, HTTPS/HSTS enforcement, and MIME sniffing protection.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

No actionable comments were generated in the recent review. 🎉


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 4 files

Confidence score: 2/5

  • Missing SECURE_PROXY_SSL_HEADER in backend/settings/production.py can cause Django to mis-detect HTTPS behind a proxy, which may break SSL redirects or security assumptions in production.
  • enable_introspection is not a valid StrawberryConfig option in backend/settings/graphql.py, so introspection disabling may not work as intended at runtime.
  • These are high-severity, production-affecting configuration issues, so the merge risk is elevated.
  • Pay close attention to backend/settings/production.py and backend/settings/graphql.py - proxy HTTPS detection and GraphQL introspection configuration.
Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="backend/settings/production.py">

<violation number="1" location="backend/settings/production.py:62">
P1: Missing `SECURE_PROXY_SSL_HEADER` setting. When running behind a load balancer or reverse proxy (common in AWS deployments), Django needs this setting to properly detect HTTPS connections. Without it, `SECURE_SSL_REDIRECT = True` can cause infinite redirect loops because Django sees all requests as HTTP.

Add this setting to inform Django about the proxy's forwarded protocol header.</violation>
</file>

<file name="backend/settings/graphql.py">

<violation number="1" location="backend/settings/graphql.py:51">
P1: `enable_introspection` is not a valid parameter for `StrawberryConfig`. According to Strawberry GraphQL documentation, introspection should be disabled using the `DisableIntrospection` extension instead.

To conditionally disable introspection in production:
```python
from strawberry.extensions import DisableIntrospection

extensions = [QueryDepthLimiter(max_depth=5), DjangoOptimizerExtension()]
if not settings.DEBUG and not getattr(settings, "IS_FUZZ_ENVIRONMENT", False):
    extensions.append(DisableIntrospection())

schema = strawberry.Schema(
    mutation=Mutation,
    query=Query,
    extensions=extensions,
)
```</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link

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 implements security hardening improvements aligned with OWASP recommendations to strengthen the application's production security posture. The changes focus on disabling GraphQL introspection in production, enforcing HTTPS/HSTS, and preventing MIME sniffing attacks. Additionally, unrelated Dockerfile optimizations consolidate RUN commands to reduce Docker layers.

Changes:

  • Disabled GraphQL introspection when DEBUG=False, with exception for fuzz testing environments
  • Added HSTS and SSL redirect settings to production environment (1 year HSTS with subdomains and preload)
  • Enabled X-Content-Type-Options: nosniff header globally via SECURE_CONTENT_TYPE_NOSNIFF
  • Consolidated multiple RUN commands in frontend Dockerfile (unrelated to security hardening)

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
backend/settings/graphql.py Adds StrawberryConfig to disable GraphQL introspection in production while preserving it for DEBUG and fuzz testing
backend/settings/production.py Configures HSTS with 1-year expiration, subdomain inclusion, preload support, and SSL redirect enforcement
backend/settings/base.py Adds SECURE_CONTENT_TYPE_NOSNIFF globally to prevent MIME type sniffing
docker/frontend/Dockerfile Consolidates RUN commands with && operators to reduce Docker layers (unrelated to security scope)

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

Comment on lines 58 to 62
# Security
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_SSL_REDIRECT = True
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

The staging environment (backend/settings/staging.py) should also include the same HSTS and SSL security settings as production. Staging environments typically serve over HTTPS with valid certificates and should mirror production security configurations to catch issues before they reach production. Consider adding the same SECURE_HSTS_SECONDS, SECURE_HSTS_INCLUDE_SUBDOMAINS, SECURE_HSTS_PRELOAD, and SECURE_SSL_REDIRECT settings to staging.py.

Copilot uses AI. Check for mistakes.
coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 6, 2026
Copy link
Collaborator

@arkid15r arkid15r left a comment

Choose a reason for hiding this comment

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

Could you resolve copilot comments?

@arkid15r arkid15r marked this pull request as draft February 7, 2026 01:36
ScienHAC and others added 2 commits February 8, 2026 16:27
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 8, 2026
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="docker/frontend/Dockerfile">

<violation number="1" location="docker/frontend/Dockerfile:13">
P0: Setting `PATH="$PNPM_HOME:$PATH"` in the same ENV instruction where `PNPM_HOME` is defined will not work correctly. In Docker, variables set in a single ENV instruction are not available for expansion within that same instruction. The `$PNPM_HOME` variable will be empty (undefined), so PATH will not include `/pnpm`, causing pnpm commands to fail.

Split this into two separate ENV instructions to ensure PNPM_HOME is defined before being used in PATH expansion.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@docker/frontend/Dockerfile`:
- Around line 8-13: The ENV block uses PNPM_HOME and PATH in the same
instruction so $PNPM_HOME won't expand; split the ENV into two instructions:
first define PNPM_HOME (and any cache vars that don't reference PNPM_HOME) using
ENV PNPM_HOME="/pnpm" (and keep NPM_CACHE, APK_CACHE_DIR, APK_SYMLINK_DIR,
FORCE_COLOR if you prefer), then a second ENV to set PATH="$PNPM_HOME:$PATH" so
the PATH expansion picks up the just-defined PNPM_HOME; update the Dockerfile's
ENV instructions accordingly (look for PNPM_HOME and PATH).

coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 8, 2026
@ScienHAC ScienHAC marked this pull request as ready for review February 8, 2026 11:47
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="docker/frontend/Dockerfile">

<violation number="1">
P1: The `-f` flag was removed from `rm .env`. Without it, the build will fail if `.env` doesn't exist in the standalone output. The original code used `rm -f .env` to handle this case gracefully.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Added security settings for HSTS and SSL redirection.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@backend/settings/staging.py`:
- Around line 54-59: The staging settings are missing SECURE_PROXY_SSL_HEADER
which causes SSL redirect loops behind a TLS-terminating proxy; add the same
header tuple used in production (SECURE_PROXY_SSL_HEADER =
("HTTP_X_FORWARDED_PROTO", "https")) into staging.py alongside
SECURE_HSTS_SECONDS / SECURE_SSL_REDIRECT so Django treats X-Forwarded-Proto as
the scheme when SECURE_SSL_REDIRECT is True, matching the behavior of
production.py.

coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 8, 2026
@ScienHAC
Copy link
Contributor Author

ScienHAC commented Feb 8, 2026

Let me know is this correct, i apply all the required changes into them..

SLACK_COMMANDS_ENABLED = True
SLACK_EVENTS_ENABLED = True

# Security
Copy link
Collaborator

Choose a reason for hiding this comment

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

A believe a more reasonable default would be to have security settings shared by staging/production via base.py and have them overridden in local as needed.

DjangoOptimizerExtension(),
]

if not settings.DEBUG and not getattr(settings, "IS_FUZZ_ENVIRONMENT", False):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's make settings.IS_FUZZ_ENVIRONMENT work.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 7 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="backend/settings/base.py">

<violation number="1" location="backend/settings/base.py:239">
P1: `SECURE_SSL_REDIRECT = True` in the Base configuration is not overridden in `test.py`. The local, e2e, and fuzz configurations all correctly override this to `False`, but the test configuration does not. This will cause Django's `SecurityMiddleware` to 301-redirect all HTTP requests to HTTPS during tests, which can break test assertions expecting direct responses.

Either add `SECURE_SSL_REDIRECT = False` to `backend/settings/test.py`, or move `SECURE_SSL_REDIRECT = True` out of `base.py` and into only the configurations that need it (production, staging).</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

SECURE_HSTS_PRELOAD = True
SECURE_HSTS_SECONDS = 31536000
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_SSL_REDIRECT = True
Copy link
Contributor

Choose a reason for hiding this comment

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

P1: SECURE_SSL_REDIRECT = True in the Base configuration is not overridden in test.py. The local, e2e, and fuzz configurations all correctly override this to False, but the test configuration does not. This will cause Django's SecurityMiddleware to 301-redirect all HTTP requests to HTTPS during tests, which can break test assertions expecting direct responses.

Either add SECURE_SSL_REDIRECT = False to backend/settings/test.py, or move SECURE_SSL_REDIRECT = True out of base.py and into only the configurations that need it (production, staging).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/settings/base.py, line 239:

<comment>`SECURE_SSL_REDIRECT = True` in the Base configuration is not overridden in `test.py`. The local, e2e, and fuzz configurations all correctly override this to `False`, but the test configuration does not. This will cause Django's `SecurityMiddleware` to 301-redirect all HTTP requests to HTTPS during tests, which can break test assertions expecting direct responses.

Either add `SECURE_SSL_REDIRECT = False` to `backend/settings/test.py`, or move `SECURE_SSL_REDIRECT = True` out of `base.py` and into only the configurations that need it (production, staging).</comment>

<file context>
@@ -231,3 +232,8 @@ class Base(Configuration):
+    SECURE_HSTS_PRELOAD = True
+    SECURE_HSTS_SECONDS = 31536000
+    SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
+    SECURE_SSL_REDIRECT = True
</file context>

@codecov
Copy link

codecov bot commented Feb 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.39%. Comparing base (9c43ec9) to head (d28bf41).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #3799   +/-   ##
=======================================
  Coverage   95.39%   95.39%           
=======================================
  Files         463      463           
  Lines       14540    14540           
  Branches     2017     2017           
=======================================
  Hits        13871    13871           
  Misses        328      328           
  Partials      341      341           
Flag Coverage Δ
backend 95.67% <ø> (ø)
frontend 94.64% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9c43ec9...d28bf41. Read the comment docs.

🚀 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@backend/settings/base.py`:
- Line 31: Update the test and fuzz settings to disable production-only security
flags that break HTTP test runs: in test.py add overrides
CSRF_COOKIE_SECURE=False, SESSION_COOKIE_SECURE=False,
SECURE_SSL_REDIRECT=False, SECURE_HSTS_SECONDS=0 and
SECURE_PROXY_SSL_HEADER=None (matching local.py/e2e.py pattern) so tests won’t
be redirected or drop cookies; in fuzz.py add CSRF_COOKIE_SECURE=False and
SESSION_COOKIE_SECURE=False in addition to its existing
SECURE_SSL_REDIRECT/SECURE_HSTS_SECONDS overrides to ensure fuzz tests run over
plain HTTP.

In `@backend/settings/fuzz.py`:
- Around line 18-20: The fuzz settings inherit CSRF_COOKIE_SECURE=True and
SESSION_COOKIE_SECURE=True from Base which prevents cookies over plain HTTP; in
backend/settings/fuzz.py add explicit overrides CSRF_COOKIE_SECURE = False and
SESSION_COOKIE_SECURE = False (alongside SECURE_HSTS_SECONDS,
SECURE_PROXY_SSL_HEADER, SECURE_SSL_REDIRECT) so the fuzz environment will send
cookies over http://localhost:9500 and CSRF/session handling works during tests.

@sonarqubecloud
Copy link

@arkid15r arkid15r enabled auto-merge February 12, 2026 06:33
@arkid15r arkid15r added this pull request to the merge queue Feb 12, 2026
Merged via the queue into OWASP:main with commit d273a71 Feb 12, 2026
36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enhance production security with GraphQL, HSTS, and HTTP header hardening

3 participants