Skip to content

fix:zap baseline security headers#4137

Merged
arkid15r merged 3 commits intoOWASP:mainfrom
HarshitVerma109:fix/zap-baseline-security-headers
Mar 3, 2026
Merged

fix:zap baseline security headers#4137
arkid15r merged 3 commits intoOWASP:mainfrom
HarshitVerma109:fix/zap-baseline-security-headers

Conversation

@HarshitVerma109
Copy link
Contributor

@HarshitVerma109 HarshitVerma109 commented Mar 1, 2026

Proposed change

Resolves #4090
Migrate security headers from Nginx proxy into Next.js config and ALB listener to fix ZAP baseline scan failures on staging.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 1, 2026

Summary by CodeRabbit

  • Chores

    • Added security-related HTTP headers to frontend configuration and load balancer, including Content-Security-Policy and additional protection mechanisms
  • Tests

    • Added comprehensive validation tests for all security header configurations

Walkthrough

Adds HTTP security headers across frontend (Next.js) and backend (ALB) and introduces tests: Content-Security-Policy, Strict-Transport-Security (HSTS), X-Frame-Options, X-Content-Type-Options, and several cross-origin and referrer headers.

Changes

Cohort / File(s) Summary
Frontend Security Headers
frontend/next.config.ts
Adds a securityHeaders set and an async headers() method that applies those headers (CSP, COEP, COOP, CORP, Permissions-Policy, Referrer-Policy, X-Permitted-Cross-Domain-Policies) to all routes (/:path*).
Infrastructure ALB Headers
infrastructure/modules/alb/main.tf
Adds local.content_security_policy and configures the HTTPS listener to set routing HTTP response headers: Content-Security-Policy, Strict-Transport-Security, X-Content-Type-Options, X-Frame-Options (and related header attributes).
Infrastructure Tests
infrastructure/modules/alb/tests/alb.tftest.hcl
Adds multiple tests verifying the ALB HTTPS listener sets HSTS, X-Frame-Options (DENY), X-Content-Type-Options (nosniff), Content-Security-Policy (matches local CSP), presence of all security headers, and CSP directive composition/count.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • kasya
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix:zap baseline security headers' clearly identifies the main change—adding security headers to fix ZAP baseline scan failures.
Description check ✅ Passed The description directly references issue #4090 and clearly explains the objective of migrating security headers from Nginx to Next.js and ALB.
Linked Issues check ✅ Passed The PR addresses the primary coding requirement from issue #4090: implementing security headers to remediate ZAP baseline scan failures. Headers are added to both Next.js config and ALB listener, aligning with the stated objective.
Out of Scope Changes check ✅ Passed All changes are within scope: Next.js security headers, ALB listener headers, and corresponding tests. However, the implementation has notable design concerns flagged in review comments regarding CSP duplication across frontend and infrastructure code, and potential production CSP weaknesses.
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 unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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.

cubic-dev-ai[bot]
cubic-dev-ai bot previously approved these changes Mar 1, 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.

No issues found across 3 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

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.

🧹 Nitpick comments (2)
infrastructure/modules/alb/main.tf (1)

35-36: CSP alignment with Next.js is correct.

The Content-Security-Policy here matches the production CSP in next.config.ts (excluding the local-only 'unsafe-eval' directive). This dual-layer approach ensures headers are set even if one layer fails.

Consider extracting this policy to a shared location or adding a comment referencing frontend/next.config.ts to help maintainers keep them in sync during future updates.

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

In `@infrastructure/modules/alb/main.tf` around lines 35 - 36, The
Content-Security-Policy string assigned to content_security_policy (near
backend_path_chunks/local.backend_paths usage) should be made maintainable:
either extract the CSP into a shared variable or module so both Terraform and
Next.js read the same source, or at minimum add an inline comment above the
content_security_policy assignment referencing the canonical policy in
frontend/next.config.ts so future changes stay in sync; update references
(content_security_policy) accordingly and ensure any local-only directives like
'unsafe-eval' remain excluded here.
infrastructure/modules/alb/tests/alb.tftest.hcl (1)

245-252: CSP test provides strong validation but requires synchronization.

The hardcoded CSP string in the assertion (line 249) must stay in sync with local.content_security_policy in main.tf. While this creates maintenance overhead, it also ensures any CSP modifications are intentional and reviewed. Consider adding a comment noting this dependency.

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

In `@infrastructure/modules/alb/tests/alb.tftest.hcl` around lines 245 - 252, The
CSP assertion in the test run "test_https_listener_sets_csp_header" hardcodes a
value that must remain synchronized with local.content_security_policy in
main.tf; update the test to add a clear comment above the assert referencing
local.content_security_policy and instructing maintainers to update this
hardcoded string whenever local.content_security_policy changes (or to derive
the expected value from the same source in future), and mention
aws_lb_listener.https as the resource validated so reviewers know where to check
and keep in sync.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@infrastructure/modules/alb/main.tf`:
- Around line 35-36: The Content-Security-Policy string assigned to
content_security_policy (near backend_path_chunks/local.backend_paths usage)
should be made maintainable: either extract the CSP into a shared variable or
module so both Terraform and Next.js read the same source, or at minimum add an
inline comment above the content_security_policy assignment referencing the
canonical policy in frontend/next.config.ts so future changes stay in sync;
update references (content_security_policy) accordingly and ensure any
local-only directives like 'unsafe-eval' remain excluded here.

In `@infrastructure/modules/alb/tests/alb.tftest.hcl`:
- Around line 245-252: The CSP assertion in the test run
"test_https_listener_sets_csp_header" hardcodes a value that must remain
synchronized with local.content_security_policy in main.tf; update the test to
add a clear comment above the assert referencing local.content_security_policy
and instructing maintainers to update this hardcoded string whenever
local.content_security_policy changes (or to derive the expected value from the
same source in future), and mention aws_lb_listener.https as the resource
validated so reviewers know where to check and keep in sync.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6d1c458 and 60db611.

📒 Files selected for processing (3)
  • frontend/next.config.ts
  • infrastructure/modules/alb/main.tf
  • infrastructure/modules/alb/tests/alb.tftest.hcl

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 1, 2026
@HarshitVerma109
Copy link
Contributor Author

@arkid15r, please review it and let me know if any changes are required.

@preeeetham
Copy link
Contributor

Strong security improvement — adding CSP, HSTS, X-Frame-Options, and the other hardening headers at both the Next.js and ALB layers is good defense-in-depth. A few things I'd flag:

The CSP policy is defined in two places — frontend/next.config.ts as a structured array and infrastructure/modules/alb/main.tf as a single string. These need to stay in sync manually, and they're in completely different formats (JS array vs Terraform string). If someone updates the CSP in the Next.js config but forgets the ALB, the headers will conflict depending on which one takes precedence. Consider extracting the CSP to a shared config file or at minimum adding a comment in each location pointing to the other one.

The CSP includes 'unsafe-inline' for both script-src and style-src in production. For styles, 'unsafe-inline' is sometimes unavoidable with CSS-in-JS frameworks, but for scripts it significantly weakens the CSP since it allows inline script injection — which is exactly what CSP is supposed to prevent. The isLocal check only gates 'unsafe-eval', not 'unsafe-inline'. If the app doesn't rely on inline scripts in production, this should be removed. If it does (e.g., Next.js injects inline scripts for hydration), then adding a nonce-based approach would be more secure.

The Cross-Origin-Embedder-Policy: credentialless is a relatively new header. It's less restrictive than require-corp but still might break third-party resources that don't support it — worth verifying that the OpenStreetMap tiles, PostHog scripts, and Google Analytics all work correctly with this header enabled.

On the Terraform side, the ALB listener now sets headers directly on the listener resource. This is clean, but note that these headers will apply to all responses including error pages and redirects. The X-Frame-Options: DENY is good but it's redundant with frame-ancestors 'none' in the CSP — browsers that support CSP will use frame-ancestors and ignore X-Frame-Options. Not a problem, just redundant for modern browsers (but good to keep for older ones).

The test coverage for the ALB headers is solid — assertions for each header value are a nice touch.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 3, 2026

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.

🧹 Nitpick comments (2)
infrastructure/modules/alb/tests/alb.tftest.hcl (2)

284-289: Directive count assertion is brittle for future-safe CSP additions.

Using an exact == 11 can fail on benign hardening changes. Prefer a minimum threshold plus normalized splitting.

♻️ Proposed robustness tweak
 run "test_csp_directive_count" {
   command = plan

   assert {
-    condition     = length(regexall("[^;]+", trimspace(local.content_security_policy))) == 11
-    error_message = "Content-Security-Policy must contain 11 directives."
+    condition = length([
+      for directive in split(";", local.content_security_policy) :
+      trimspace(directive) if trimspace(directive) != ""
+    ]) >= 11
+    error_message = "Content-Security-Policy must contain at least 11 directives."
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@infrastructure/modules/alb/tests/alb.tftest.hcl` around lines 284 - 289, The
CSP directive count check in run "test_csp_directive_count" is brittle because
it asserts equality to 11; change the assertion to enforce a minimum (e.g., >=
11) and normalize splitting by trimming and filtering out empty entries before
counting so harmless future directives don't break the test — update the
condition that uses regexall/trimspace/local.content_security_policy to trim
each matched segment and count only non-empty directives, asserting length(...)
>= 11 instead of == 11.

268-281: Add a negative guard for insecure script-src tokens.

The current checks ensure directive presence, but they won’t catch accidental weakening of CSP via unsafe-inline / unsafe-eval in script-src.

♻️ Proposed test hardening
 run "test_csp_contains_required_directives" {
   command = plan

   assert {
     condition = alltrue([
       strcontains(local.content_security_policy, "default-src 'self'"),
       strcontains(local.content_security_policy, "object-src 'none'"),
       strcontains(local.content_security_policy, "frame-ancestors 'none'"),
       strcontains(local.content_security_policy, "script-src"),
       strcontains(local.content_security_policy, "style-src"),
-      strcontains(local.content_security_policy, "base-uri 'self'")
+      strcontains(local.content_security_policy, "base-uri 'self'"),
+      !can(regex("script-src[^;]*'unsafe-inline'", local.content_security_policy)),
+      !can(regex("script-src[^;]*'unsafe-eval'", local.content_security_policy))
     ])
     error_message = "Content-Security-Policy must contain required security directives."
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@infrastructure/modules/alb/tests/alb.tftest.hcl` around lines 268 - 281, The
test run "test_csp_contains_required_directives" currently checks presence of
directives in local.content_security_policy but lacks negative guards for
insecure script-src tokens; update the assert conditions to also ensure
local.content_security_policy does NOT contain "unsafe-inline" and "unsafe-eval"
(e.g., add not(strcontains(local.content_security_policy, "unsafe-inline")) and
not(strcontains(local.content_security_policy, "unsafe-eval"))), so the test
fails if the CSP has those weakening tokens in script-src.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@infrastructure/modules/alb/tests/alb.tftest.hcl`:
- Around line 284-289: The CSP directive count check in run
"test_csp_directive_count" is brittle because it asserts equality to 11; change
the assertion to enforce a minimum (e.g., >= 11) and normalize splitting by
trimming and filtering out empty entries before counting so harmless future
directives don't break the test — update the condition that uses
regexall/trimspace/local.content_security_policy to trim each matched segment
and count only non-empty directives, asserting length(...) >= 11 instead of ==
11.
- Around line 268-281: The test run "test_csp_contains_required_directives"
currently checks presence of directives in local.content_security_policy but
lacks negative guards for insecure script-src tokens; update the assert
conditions to also ensure local.content_security_policy does NOT contain
"unsafe-inline" and "unsafe-eval" (e.g., add
not(strcontains(local.content_security_policy, "unsafe-inline")) and
not(strcontains(local.content_security_policy, "unsafe-eval"))), so the test
fails if the CSP has those weakening tokens in script-src.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 60db611 and 8826054.

📒 Files selected for processing (3)
  • frontend/next.config.ts
  • infrastructure/modules/alb/main.tf
  • infrastructure/modules/alb/tests/alb.tftest.hcl
🚧 Files skipped from review as they are similar to previous changes (2)
  • infrastructure/modules/alb/main.tf
  • frontend/next.config.ts

@codecov
Copy link

codecov bot commented Mar 3, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.80%. Comparing base (796a67e) to head (8826054).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #4137   +/-   ##
=======================================
  Coverage   99.80%   99.80%           
=======================================
  Files         519      519           
  Lines       16031    16031           
  Branches     2186     2143   -43     
=======================================
  Hits        16000    16000           
  Misses         26       26           
  Partials        5        5           
Flag Coverage Δ
backend 99.78% <ø> (ø)
frontend 99.87% <ø> (ø)

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 796a67e...8826054. 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.

@arkid15r arkid15r added this pull request to the merge queue Mar 3, 2026
Merged via the queue into OWASP:main with commit 7e7054b Mar 3, 2026
38 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.

Fix staging post-migration ZAP findings

3 participants