chore: security changes #3007

Merged
mfreeman451 merged 5 commits from refs/pull/3007/head into staging 2026-03-03 00:17:18 +00:00
mfreeman451 commented 2026-03-02 22:04:33 +00:00 (Migrated from github.com)
Owner

Imported from GitHub pull request.

Original GitHub pull request: #2981
Original author: @mfreeman451
Original URL: https://github.com/carverauto/serviceradar/pull/2981
Original created: 2026-03-02T22:04:33Z
Original updated: 2026-03-03T00:17:21Z
Original head: carverauto/serviceradar:chore/sec-scan
Original base: staging
Original merged: 2026-03-03T00:17:18Z by @mfreeman451

IMPORTANT: Please sign the Developer Certificate of Origin

Thank you for your contribution to ServiceRadar. Please note, when contributing, the developer must include
a DCO sign-off statement indicating the DCO acceptance in one commit message. Here
is an example DCO Signed-off-by line in a commit message:

Signed-off-by: J. Doe <j.doe@domain.com>

Describe your changes

Code checklist before requesting a review

  • I have signed the DCO?
  • The build completes without errors?
  • All tests are passing when running make test?
Imported from GitHub pull request. Original GitHub pull request: #2981 Original author: @mfreeman451 Original URL: https://github.com/carverauto/serviceradar/pull/2981 Original created: 2026-03-02T22:04:33Z Original updated: 2026-03-03T00:17:21Z Original head: carverauto/serviceradar:chore/sec-scan Original base: staging Original merged: 2026-03-03T00:17:18Z by @mfreeman451 --- ## IMPORTANT: Please sign the Developer Certificate of Origin Thank you for your contribution to ServiceRadar. Please note, when contributing, the developer must include a [DCO sign-off statement]( https://developercertificate.org/) indicating the DCO acceptance in one commit message. Here is an example DCO Signed-off-by line in a commit message: ``` Signed-off-by: J. Doe <j.doe@domain.com> ``` ## Describe your changes ## Issue ticket number and link ## Code checklist before requesting a review - [ ] I have signed the DCO? - [ ] The build completes without errors? - [ ] All tests are passing when running make test?
qodo-code-review[bot] commented 2026-03-02 22:04:55 +00:00 (Migrated from github.com)
Author
Owner

Imported GitHub PR comment.

Original author: @qodo-code-review[bot]
Original URL: https://github.com/carverauto/serviceradar/pull/2981#issuecomment-3987183256
Original created: 2026-03-02T22:04:55Z

Review Summary by Qodo

Harden web-ng security controls for SSRF, XSS, and SAML validation

🐞 Bug fix ✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add outbound URL validation to prevent SSRF attacks in OIDC, SAML, and gateway auth metadata/JWKS
  fetches
• Implement cryptographic SAML signature verification and stricter assertion validation (issuer,
  audience, temporal constraints)
• Sanitize plugin markdown HTML to block dangerous URL schemes and unsafe event attributes before
  rendering
• Harden Content Security Policy by removing 'unsafe-inline' from script-src directive
• Add comprehensive security tests for XSS payloads, SAML validation, and URL policy enforcement
Diagram
flowchart LR
  A["Outbound URL Policy"] -->|validates| B["OIDC Discovery"]
  A -->|validates| C["SAML Metadata Fetch"]
  A -->|validates| D["Gateway JWKS Fetch"]
  E["SAML Assertion Validator"] -->|cryptographic verify| F["SAML Controller"]
  G["Markdown Sanitizer"] -->|strips unsafe| H["Plugin Results Component"]
  I["Hardened CSP"] -->|removes unsafe-inline| J["Router"]
Grey Divider

File Changes

1. elixir/web-ng/config/config.exs ⚙️ Configuration changes +3/-0

Add security configuration for metadata and SAML

elixir/web-ng/config/config.exs


2. elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex ✨ Enhancement +116/-0

New module for SSRF prevention and URL validation

elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex


3. elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex ✨ Enhancement +112/-0

New module for cryptographic SAML assertion validation

elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex


View more (16)
4. elixir/web-ng/lib/serviceradar_web_ng_web/auth/oidc_client.ex 🐞 Bug fix +37/-12

Integrate outbound URL policy into OIDC metadata fetches

elixir/web-ng/lib/serviceradar_web_ng_web/auth/oidc_client.ex


5. elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_strategy.ex ✨ Enhancement +49/-5

Add URL validation and IdP entity ID extraction for SAML

elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_strategy.ex


6. elixir/web-ng/lib/serviceradar_web_ng_web/components/plugin_results.ex 🐞 Bug fix +77/-2

Sanitize markdown HTML to prevent XSS attacks

elixir/web-ng/lib/serviceradar_web_ng_web/components/plugin_results.ex


7. elixir/web-ng/lib/serviceradar_web_ng_web/controllers/saml_controller.ex 🐞 Bug fix +87/-165

Replace permissive SAML validation with cryptographic verification

elixir/web-ng/lib/serviceradar_web_ng_web/controllers/saml_controller.ex


8. elixir/web-ng/lib/serviceradar_web_ng_web/live/settings/authentication_live.ex ✨ Enhancement +53/-28

Apply outbound URL policy to auth settings test endpoints

elixir/web-ng/lib/serviceradar_web_ng_web/live/settings/authentication_live.ex


9. elixir/web-ng/lib/serviceradar_web_ng_web/plugs/gateway_auth.ex 🐞 Bug fix +18/-5

Add URL validation to gateway JWKS fetch operations

elixir/web-ng/lib/serviceradar_web_ng_web/plugs/gateway_auth.ex


10. elixir/web-ng/lib/serviceradar_web_ng_web/router.ex 🐞 Bug fix +1/-1

Remove unsafe-inline from Content Security Policy

elixir/web-ng/lib/serviceradar_web_ng_web/router.ex


11. elixir/web-ng/test/phoenix/auth/outbound_url_policy_test.exs 🧪 Tests +25/-0

Test outbound URL policy validation and SSRF prevention

elixir/web-ng/test/phoenix/auth/outbound_url_policy_test.exs


12. elixir/web-ng/test/phoenix/auth/saml_assertion_validator_test.exs 🧪 Tests +154/-0

Test SAML assertion validation and temporal constraints

elixir/web-ng/test/phoenix/auth/saml_assertion_validator_test.exs


13. elixir/web-ng/test/phoenix/components/plugin_results_test.exs 🧪 Tests +20/-0

Test markdown sanitization for XSS payloads

elixir/web-ng/test/phoenix/components/plugin_results_test.exs


14. elixir/web-ng/test/phoenix/controllers/security_headers_test.exs 🧪 Tests +11/-0

Test hardened Content Security Policy headers

elixir/web-ng/test/phoenix/controllers/security_headers_test.exs


15. openspec/changes/harden-web-ng-security-controls/design.md 📝 Documentation +50/-0

Document security hardening design decisions and rationale

openspec/changes/harden-web-ng-security-controls/design.md


16. openspec/changes/harden-web-ng-security-controls/proposal.md 📝 Documentation +42/-0

Document security findings and proposed changes

openspec/changes/harden-web-ng-security-controls/proposal.md


17. openspec/changes/harden-web-ng-security-controls/specs/web-ng-security-controls/spec.md 📝 Documentation +43/-0

Define security requirements and test scenarios

openspec/changes/harden-web-ng-security-controls/specs/web-ng-security-controls/spec.md


18. openspec/changes/harden-web-ng-security-controls/tasks.md 📝 Documentation +15/-0

Track implementation tasks and verification steps

openspec/changes/harden-web-ng-security-controls/tasks.md


19. openspec/changes/harden-web-ng-security-controls/verification.md 📝 Documentation +18/-0

Document security verification and test results

openspec/changes/harden-web-ng-security-controls/verification.md


Grey Divider

Qodo Logo

Imported GitHub PR comment. Original author: @qodo-code-review[bot] Original URL: https://github.com/carverauto/serviceradar/pull/2981#issuecomment-3987183256 Original created: 2026-03-02T22:04:55Z --- <h3>Review Summary by Qodo</h3> Harden web-ng security controls for SSRF, XSS, and SAML validation <code>🐞 Bug fix</code> <code>✨ Enhancement</code> <img src="https://www.qodo.ai/wp-content/uploads/2025/11/light-grey-line.svg" height="10%" alt="Grey Divider"> <h3>Walkthroughs</h3> <details open> <summary>Description</summary> <br/> <pre> • Add outbound URL validation to prevent SSRF attacks in OIDC, SAML, and gateway auth metadata/JWKS fetches • Implement cryptographic SAML signature verification and stricter assertion validation (issuer, audience, temporal constraints) • Sanitize plugin markdown HTML to block dangerous URL schemes and unsafe event attributes before rendering • Harden Content Security Policy by removing <b><i>&#x27;unsafe-inline&#x27;</i></b> from script-src directive • Add comprehensive security tests for XSS payloads, SAML validation, and URL policy enforcement </pre> </details> <details> <summary>Diagram</summary> <br/> > ```mermaid flowchart LR A["Outbound URL Policy"] -->|validates| B["OIDC Discovery"] A -->|validates| C["SAML Metadata Fetch"] A -->|validates| D["Gateway JWKS Fetch"] E["SAML Assertion Validator"] -->|cryptographic verify| F["SAML Controller"] G["Markdown Sanitizer"] -->|strips unsafe| H["Plugin Results Component"] I["Hardened CSP"] -->|removes unsafe-inline| J["Router"] ``` </details> <img src="https://www.qodo.ai/wp-content/uploads/2025/11/light-grey-line.svg" height="10%" alt="Grey Divider"> <h3>File Changes</h3> <details> <summary>1. elixir/web-ng/config/config.exs <code>⚙️ Configuration changes</code> <code> +3/-0 </code> </summary> <br/> >Add security configuration for metadata and SAML > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-aab5dc799c670fbfa642bf84ecbfb3759de01a2bbde35f7675841c7e1d59cd17'> elixir/web-ng/config/config.exs </a> <hr/> </details> <details> <summary>2. elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex <code>✨ Enhancement</code> <code> +116/-0 </code> </summary> <br/> >New module for SSRF prevention and URL validation > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-654bac0d43d2ab7f8c8587ea12f3e9f1a33f4d41aed0c05c8b4d005fc03d8f07'> elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex </a> <hr/> </details> <details> <summary>3. elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex <code>✨ Enhancement</code> <code> +112/-0 </code> </summary> <br/> >New module for cryptographic SAML assertion validation > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-989aa204a287fcf5fe15da4d50fccc97c43f25b5c2d96b990addc7d881ae5e50'> elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex </a> <hr/> </details> <details><summary><ins><strong>View more (16)</strong></ins></summary><br/> <details> <summary>4. elixir/web-ng/lib/serviceradar_web_ng_web/auth/oidc_client.ex <code>🐞 Bug fix</code> <code> +37/-12 </code> </summary> <br/> >Integrate outbound URL policy into OIDC metadata fetches > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-1c53e2f5c764ff8d510f7ed88fe5c05cbab8660514d7e006b53858db22f64b50'> elixir/web-ng/lib/serviceradar_web_ng_web/auth/oidc_client.ex </a> <hr/> </details> <details> <summary>5. elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_strategy.ex <code>✨ Enhancement</code> <code> +49/-5 </code> </summary> <br/> >Add URL validation and IdP entity ID extraction for SAML > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-5e9e8ce7e1d30db7f8736c0bab2607d871fafbbf37e61b432e5430ee6c34afe6'> elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_strategy.ex </a> <hr/> </details> <details> <summary>6. elixir/web-ng/lib/serviceradar_web_ng_web/components/plugin_results.ex <code>🐞 Bug fix</code> <code> +77/-2 </code> </summary> <br/> >Sanitize markdown HTML to prevent XSS attacks > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-3bd4d99912a9be0c6557786637636d184a1f8659e01ca6218828ca4e38e067b2'> elixir/web-ng/lib/serviceradar_web_ng_web/components/plugin_results.ex </a> <hr/> </details> <details> <summary>7. elixir/web-ng/lib/serviceradar_web_ng_web/controllers/saml_controller.ex <code>🐞 Bug fix</code> <code> +87/-165 </code> </summary> <br/> >Replace permissive SAML validation with cryptographic verification > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-096a0d46ceaa024024dc654c4a68e7afcc57b185a0ec83ad24798b9b489dd912'> elixir/web-ng/lib/serviceradar_web_ng_web/controllers/saml_controller.ex </a> <hr/> </details> <details> <summary>8. elixir/web-ng/lib/serviceradar_web_ng_web/live/settings/authentication_live.ex <code>✨ Enhancement</code> <code> +53/-28 </code> </summary> <br/> >Apply outbound URL policy to auth settings test endpoints > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-b0e4517b40f6eec36d223f474d74723c5a6696644860115f547d1d946832de8b'> elixir/web-ng/lib/serviceradar_web_ng_web/live/settings/authentication_live.ex </a> <hr/> </details> <details> <summary>9. elixir/web-ng/lib/serviceradar_web_ng_web/plugs/gateway_auth.ex <code>🐞 Bug fix</code> <code> +18/-5 </code> </summary> <br/> >Add URL validation to gateway JWKS fetch operations > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-d4be1505452bcdf4050408d19a85a0e64d504e26aa3eaf16f97c7e7e7b8ff614'> elixir/web-ng/lib/serviceradar_web_ng_web/plugs/gateway_auth.ex </a> <hr/> </details> <details> <summary>10. elixir/web-ng/lib/serviceradar_web_ng_web/router.ex <code>🐞 Bug fix</code> <code> +1/-1 </code> </summary> <br/> >Remove unsafe-inline from Content Security Policy > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-f719028352c99f517b05b70160cdc240679b52dd6c3c654fdd8b48048f56f2ce'> elixir/web-ng/lib/serviceradar_web_ng_web/router.ex </a> <hr/> </details> <details> <summary>11. elixir/web-ng/test/phoenix/auth/outbound_url_policy_test.exs <code>🧪 Tests</code> <code> +25/-0 </code> </summary> <br/> >Test outbound URL policy validation and SSRF prevention > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-87abd0424b21d9f0b7b0c05f426fa7bdc471b4e40a2875b4075edbbf1738136e'> elixir/web-ng/test/phoenix/auth/outbound_url_policy_test.exs </a> <hr/> </details> <details> <summary>12. elixir/web-ng/test/phoenix/auth/saml_assertion_validator_test.exs <code>🧪 Tests</code> <code> +154/-0 </code> </summary> <br/> >Test SAML assertion validation and temporal constraints > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-90351be78b8d88b721655147a96dae3649eed5e2c5e29de367cc1d2a765dd510'> elixir/web-ng/test/phoenix/auth/saml_assertion_validator_test.exs </a> <hr/> </details> <details> <summary>13. elixir/web-ng/test/phoenix/components/plugin_results_test.exs <code>🧪 Tests</code> <code> +20/-0 </code> </summary> <br/> >Test markdown sanitization for XSS payloads > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-0297bf63bd3e85fd35b777242f6a40ca66df4b2016a244f7776bec70a823bf95'> elixir/web-ng/test/phoenix/components/plugin_results_test.exs </a> <hr/> </details> <details> <summary>14. elixir/web-ng/test/phoenix/controllers/security_headers_test.exs <code>🧪 Tests</code> <code> +11/-0 </code> </summary> <br/> >Test hardened Content Security Policy headers > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-52d55e52ee0fc16bea5605b0ae536fbd8334e53803d643144cd6342a89dc3058'> elixir/web-ng/test/phoenix/controllers/security_headers_test.exs </a> <hr/> </details> <details> <summary>15. openspec/changes/harden-web-ng-security-controls/design.md <code>📝 Documentation</code> <code> +50/-0 </code> </summary> <br/> >Document security hardening design decisions and rationale > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-cef11f62b7df31175307c0c26dbcd57b5384917567eab4f81446b0e25db98d2c'> openspec/changes/harden-web-ng-security-controls/design.md </a> <hr/> </details> <details> <summary>16. openspec/changes/harden-web-ng-security-controls/proposal.md <code>📝 Documentation</code> <code> +42/-0 </code> </summary> <br/> >Document security findings and proposed changes > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-9d3e2a3b72b831f63aeb170739f8710669ecb2b00b1fa29748e3bb4328d60880'> openspec/changes/harden-web-ng-security-controls/proposal.md </a> <hr/> </details> <details> <summary>17. openspec/changes/harden-web-ng-security-controls/specs/web-ng-security-controls/spec.md <code>📝 Documentation</code> <code> +43/-0 </code> </summary> <br/> >Define security requirements and test scenarios > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-ba7721ccf3220028b39490c3548a6c8abc3281c4a2d536326adcc030635fe822'> openspec/changes/harden-web-ng-security-controls/specs/web-ng-security-controls/spec.md </a> <hr/> </details> <details> <summary>18. openspec/changes/harden-web-ng-security-controls/tasks.md <code>📝 Documentation</code> <code> +15/-0 </code> </summary> <br/> >Track implementation tasks and verification steps > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-30bf61328da2996bd2f78efb89a14057b2b4a5eca5412f205fc0e897066f1c66'> openspec/changes/harden-web-ng-security-controls/tasks.md </a> <hr/> </details> <details> <summary>19. openspec/changes/harden-web-ng-security-controls/verification.md <code>📝 Documentation</code> <code> +18/-0 </code> </summary> <br/> >Document security verification and test results > ><a href='https://github.com/carverauto/serviceradar/pull/2981/files#diff-9f2f40a036413b3be6a130808fe8bf50cb41c8d629a3113186c395d18673f6a5'> openspec/changes/harden-web-ng-security-controls/verification.md </a> <hr/> </details> </details> <img src="https://www.qodo.ai/wp-content/uploads/2025/11/light-grey-line.svg" height="10%" alt="Grey Divider"> <a href="https://www.qodo.ai"><img src="https://www.qodo.ai/wp-content/uploads/2025/03/qodo-logo.svg" width="80" alt="Qodo Logo"></a>
qodo-code-review[bot] commented 2026-03-02 22:04:56 +00:00 (Migrated from github.com)
Author
Owner

Imported GitHub PR comment.

Original author: @qodo-code-review[bot]
Original URL: https://github.com/carverauto/serviceradar/pull/2981#issuecomment-3987183330
Original created: 2026-03-02T22:04:56Z

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider
Action required
1. SAML datetime tuple mismatch 🐞 Bug ✓ Correctness
Description
SAMLAssertionValidator.parse_dt/1 returns the 3-tuple from DateTime.from_iso8601/1, but
validate_time_window/3 matches only a 2-tuple, causing otherwise-valid assertions to fail with
:invalid_assertion_time (breaking SAML authentication). The new saml_assertion_validator_test.exs
"accepts valid assertion" test should fail under this implementation.
Code

elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex[R100-102]

+  defp parse_dt(value) when is_binary(value) and value != "" do
+    DateTime.from_iso8601(value)
+  end
Evidence
validate_time_window/3 requires parse_dt/1 to return {:ok, DateTime.t()} (2-tuple), but parse_dt/1
currently returns DateTime.from_iso8601/1 directly, which elsewhere in this repo is matched as {:ok,
dt, _offset} (3-tuple). Therefore the success branch never matches and the validator falls into the
error path for valid ISO8601 inputs.

elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex[21-29]
elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex[100-104]
elixir/web-ng/lib/serviceradar_web_ng/topology/god_view_snapshot.ex[118-122]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`ServiceRadarWebNGWeb.Auth.SAMLAssertionValidator` rejects valid assertions because it pattern-matches `DateTime.from_iso8601/1` results incorrectly.
### Issue Context
`DateTime.from_iso8601/1` is used elsewhere in the repo as a 3-tuple `{:ok, dt, offset}`; the validator currently expects `{:ok, dt}`.
### Fix Focus Areas
- elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex[21-49]
- elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex[100-105]
### Suggested change
- Implement `parse_dt/1` like:
- `case DateTime.from_iso8601(value) do {:ok, dt, _} -&amp;gt; {:ok, dt}; _ -&amp;gt; :error end`
- (Alternatively) change the `case` in `validate_time_window/3` to match `{{:ok, nb, _}, {:ok, noa, _}}`.
- Ensure existing/new tests in `test/phoenix/auth/saml_assertion_validator_test.exs` pass.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. CSP blocks existing inline JS🐞 Bug ⛯ Reliability
Description
The CSP change removes 'unsafe-inline' from script-src, but root.html.heex still includes an inline
<script> block for theme initialization. Under the new CSP, browsers will block that inline script,
likely breaking theme initialization/toggling and potentially other expected JS behavior in the root
layout.
Code

elixir/web-ng/lib/serviceradar_web_ng_web/router.ex[R11-13]

 @csp "default-src 'self'; " <>
-         "script-src 'self' 'unsafe-inline' blob:; " <>
+         "script-src 'self' blob:; " <>
        "style-src 'self' 'unsafe-inline'; " <>
Evidence
The browser pipelines unconditionally set the CSP header to the @csp value, which now lacks
'unsafe-inline' for scripts. The root layout includes a plain inline <script> without a nonce/hash,
which is disallowed by this CSP and will not execute.

elixir/web-ng/lib/serviceradar_web_ng_web/router.ex[11-13]
elixir/web-ng/lib/serviceradar_web_ng_web/router.ex[24-31]
elixir/web-ng/lib/serviceradar_web_ng_web/components/layouts/root.html.heex[16-34]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
CSP no longer allows inline scripts, but the root layout still contains an inline `&amp;lt;script&amp;gt;` block. Browsers will block it under the new CSP.
### Issue Context
The CSP is set for the `:browser` and `:browser_raw_auth` pipelines via `put_secure_browser_headers`.
### Fix Focus Areas
- elixir/web-ng/lib/serviceradar_web_ng_web/router.ex[11-31]
- elixir/web-ng/lib/serviceradar_web_ng_web/components/layouts/root.html.heex[14-34]
- elixir/web-ng/assets/js/app.js[1-200]
### Suggested change
- Preferred: move the theme initialization IIFE from `root.html.heex` into `assets/js/app.js` (or another bundled asset) and remove the inline `&amp;lt;script&amp;gt;` block.
- Alternative: implement per-request CSP nonces (or script hashes) and apply them to the inline `&amp;lt;script&amp;gt;` tag plus `script-src` directive.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. URL policy DNS/IPv6 bypass🐞 Bug ⛨ Security
Description
OutboundURLPolicy only resolves IPv4 addresses (:inet) and explicitly returns :ok when DNS
resolution fails. This can allow IPv6-only hosts (including private/loopback) or
temporarily-unresolvable hosts to pass validation, undermining SSRF protections for OIDC/SAML/JWKS
fetches.
Code

elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex[R90-99]

+  defp resolve_and_validate(host) do
+    case :inet.getaddrs(String.to_charlist(host), :inet) do
+      {:ok, addrs} when is_list(addrs) ->
+        if Enum.any?(addrs, &private_or_loopback_ip?/1), do: {:error, :disallowed_host}, else: :ok
+
+      _ ->
+        # If DNS resolution is unavailable in this runtime context, keep policy deterministic
+        # and rely on explicit host/IP checks above.
+        :ok
+    end
Evidence
The policy’s DNS check is limited to IPv4 and falls back to allow on any error. Since non-IPv4
addresses are treated as disallowed only when explicitly checked, skipping inet6 resolution plus
allowing DNS failures creates a bypass path for destinations the policy intends to block.

elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex[90-100]
elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex[102-107]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`OutboundURLPolicy` currently allows URLs when DNS resolution fails and only checks IPv4 resolution, enabling bypass of SSRF protections (notably for IPv6-only hosts).
### Issue Context
This validator is used for OIDC discovery/JWKS, SAML metadata fetches, and gateway JWKS fetches.
### Fix Focus Areas
- elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex[80-100]
- elixir/web-ng/test/phoenix/auth/outbound_url_policy_test.exs[1-25]
### Suggested change
- Change `resolve_and_validate/1` to return `{:error, :disallowed_host}` (or `{:error, :invalid_url}`) on DNS failures instead of `:ok`.
- Add an `:inet6` lookup and either:
- reject any IPv6 results by default (simplest/most conservative), or
- implement explicit IPv6 private/loopback/link-local/ULA checks and reject those.
- Extend tests to cover IPv6 literals and/or IPv6-only host resolution behavior where feasible.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider
ⓘ The new review experience is currently in Beta. Learn more
Grey Divider

Qodo Logo

Imported GitHub PR comment. Original author: @qodo-code-review[bot] Original URL: https://github.com/carverauto/serviceradar/pull/2981#issuecomment-3987183330 Original created: 2026-03-02T22:04:56Z --- <h3>Code Review by Qodo</h3> <code>🐞 Bugs (3)</code> <code>📘 Rule violations (0)</code> <code>📎 Requirement gaps (0)</code> <img src="https://www.qodo.ai/wp-content/uploads/2025/11/light-grey-line.svg" height="10%" alt="Grey Divider"> <br/> <img src="https://www.qodo.ai/wp-content/uploads/2026/01/action-required.png" height="20" alt="Action required"> <details> <summary> 1. SAML datetime tuple mismatch <code>🐞 Bug</code> <code>✓ Correctness</code></summary> <br/> > <details open> ><summary>Description</summary> ><br/> > ><pre> >SAMLAssertionValidator.parse_dt/1 returns the 3-tuple from DateTime.from_iso8601/1, but >validate_time_window/3 matches only a 2-tuple, causing otherwise-valid assertions to fail with >:invalid_assertion_time (breaking SAML authentication). The new saml_assertion_validator_test.exs >&quot;accepts valid assertion&quot; test should fail under this implementation. ></pre> ></details> > <details open> ><summary>Code</summary> ><br/> > ><code>[elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex[R100-102]](https://github.com/carverauto/serviceradar/pull/2981/files#diff-989aa204a287fcf5fe15da4d50fccc97c43f25b5c2d96b990addc7d881ae5e50R100-R102)</code> > >```diff >+ defp parse_dt(value) when is_binary(value) and value != "" do >+ DateTime.from_iso8601(value) >+ end >``` ></details> > <details > ><summary>Evidence</summary> ><br/> > ><pre> >validate_time_window/3 requires parse_dt/1 to return {:ok, DateTime.t()} (2-tuple), but parse_dt/1 >currently returns DateTime.from_iso8601/1 directly, which elsewhere in this repo is matched as {:ok, >dt, _offset} (3-tuple). Therefore the success branch never matches and the validator falls into the >error path for valid ISO8601 inputs. ></pre> > > <code>[elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex[21-29]](https://github.com/carverauto/serviceradar/blob/33c70734dd7e6b24430980586cab4b4a11b3b444/elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex/#L21-L29)</code> > <code>[elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex[100-104]](https://github.com/carverauto/serviceradar/blob/33c70734dd7e6b24430980586cab4b4a11b3b444/elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex/#L100-L104)</code> > <code>[elixir/web-ng/lib/serviceradar_web_ng/topology/god_view_snapshot.ex[118-122]](https://github.com/carverauto/serviceradar/blob/33c70734dd7e6b24430980586cab4b4a11b3b444/elixir/web-ng/lib/serviceradar_web_ng/topology/god_view_snapshot.ex/#L118-L122)</code> ></details> > <details> ><summary>Agent prompt</summary> ><br/> > >``` >The issue below was found during a code review. Follow the provided context and guidance below and implement a solution > >## Issue description >`ServiceRadarWebNGWeb.Auth.SAMLAssertionValidator` rejects valid assertions because it pattern-matches `DateTime.from_iso8601/1` results incorrectly. >### Issue Context >`DateTime.from_iso8601/1` is used elsewhere in the repo as a 3-tuple `{:ok, dt, offset}`; the validator currently expects `{:ok, dt}`. >### Fix Focus Areas >- elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex[21-49] >- elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex[100-105] >### Suggested change >- Implement `parse_dt/1` like: > - `case DateTime.from_iso8601(value) do {:ok, dt, _} -&amp;gt; {:ok, dt}; _ -&amp;gt; :error end` > - (Alternatively) change the `case` in `validate_time_window/3` to match `{{:ok, nb, _}, {:ok, noa, _}}`. >- Ensure existing/new tests in `test/phoenix/auth/saml_assertion_validator_test.exs` pass. >``` > <code>ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools</code> ></details> <hr/> </details> <details> <summary> 2. <s>CSP blocks existing inline JS</s> ☑ <code>🐞 Bug</code> <code>⛯ Reliability</code></summary> <br/> > <details open> ><summary>Description</summary> ><br/> > ><pre> >The CSP change removes &#x27;unsafe-inline&#x27; from script-src, but root.html.heex still includes an inline >&lt;script&gt; block for theme initialization. Under the new CSP, browsers will block that inline script, >likely breaking theme initialization/toggling and potentially other expected JS behavior in the root >layout. ></pre> ></details> > <details open> ><summary>Code</summary> ><br/> > ><code>[elixir/web-ng/lib/serviceradar_web_ng_web/router.ex[R11-13]](https://github.com/carverauto/serviceradar/pull/2981/files#diff-f719028352c99f517b05b70160cdc240679b52dd6c3c654fdd8b48048f56f2ceR11-R13)</code> > >```diff > @csp "default-src 'self'; " <> >- "script-src 'self' 'unsafe-inline' blob:; " <> >+ "script-src 'self' blob:; " <> > "style-src 'self' 'unsafe-inline'; " <> >``` ></details> > <details > ><summary>Evidence</summary> ><br/> > ><pre> >The browser pipelines unconditionally set the CSP header to the @csp value, which now lacks >&#x27;unsafe-inline&#x27; for scripts. The root layout includes a plain inline &lt;script&gt; without a nonce/hash, >which is disallowed by this CSP and will not execute. ></pre> > > <code>[elixir/web-ng/lib/serviceradar_web_ng_web/router.ex[11-13]](https://github.com/carverauto/serviceradar/blob/33c70734dd7e6b24430980586cab4b4a11b3b444/elixir/web-ng/lib/serviceradar_web_ng_web/router.ex/#L11-L13)</code> > <code>[elixir/web-ng/lib/serviceradar_web_ng_web/router.ex[24-31]](https://github.com/carverauto/serviceradar/blob/33c70734dd7e6b24430980586cab4b4a11b3b444/elixir/web-ng/lib/serviceradar_web_ng_web/router.ex/#L24-L31)</code> > <code>[elixir/web-ng/lib/serviceradar_web_ng_web/components/layouts/root.html.heex[16-34]](https://github.com/carverauto/serviceradar/blob/33c70734dd7e6b24430980586cab4b4a11b3b444/elixir/web-ng/lib/serviceradar_web_ng_web/components/layouts/root.html.heex/#L16-L34)</code> ></details> > <details> ><summary>Agent prompt</summary> ><br/> > >``` >The issue below was found during a code review. Follow the provided context and guidance below and implement a solution > >## Issue description >CSP no longer allows inline scripts, but the root layout still contains an inline `&amp;lt;script&amp;gt;` block. Browsers will block it under the new CSP. >### Issue Context >The CSP is set for the `:browser` and `:browser_raw_auth` pipelines via `put_secure_browser_headers`. >### Fix Focus Areas >- elixir/web-ng/lib/serviceradar_web_ng_web/router.ex[11-31] >- elixir/web-ng/lib/serviceradar_web_ng_web/components/layouts/root.html.heex[14-34] >- elixir/web-ng/assets/js/app.js[1-200] >### Suggested change >- Preferred: move the theme initialization IIFE from `root.html.heex` into `assets/js/app.js` (or another bundled asset) and remove the inline `&amp;lt;script&amp;gt;` block. >- Alternative: implement per-request CSP nonces (or script hashes) and apply them to the inline `&amp;lt;script&amp;gt;` tag plus `script-src` directive. >``` > <code>ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools</code> ></details> <hr/> </details> <details> <summary> 3. <s>URL policy DNS/IPv6 bypass</s> ☑ <code>🐞 Bug</code> <code>⛨ Security</code></summary> <br/> > <details open> ><summary>Description</summary> ><br/> > ><pre> >OutboundURLPolicy only resolves IPv4 addresses (:inet) and explicitly returns :ok when DNS >resolution fails. This can allow IPv6-only hosts (including private/loopback) or >temporarily-unresolvable hosts to pass validation, undermining SSRF protections for OIDC/SAML/JWKS >fetches. ></pre> ></details> > <details open> ><summary>Code</summary> ><br/> > ><code>[elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex[R90-99]](https://github.com/carverauto/serviceradar/pull/2981/files#diff-654bac0d43d2ab7f8c8587ea12f3e9f1a33f4d41aed0c05c8b4d005fc03d8f07R90-R99)</code> > >```diff >+ defp resolve_and_validate(host) do >+ case :inet.getaddrs(String.to_charlist(host), :inet) do >+ {:ok, addrs} when is_list(addrs) -> >+ if Enum.any?(addrs, &private_or_loopback_ip?/1), do: {:error, :disallowed_host}, else: :ok >+ >+ _ -> >+ # If DNS resolution is unavailable in this runtime context, keep policy deterministic >+ # and rely on explicit host/IP checks above. >+ :ok >+ end >``` ></details> > <details > ><summary>Evidence</summary> ><br/> > ><pre> >The policy’s DNS check is limited to IPv4 and falls back to allow on any error. Since non-IPv4 >addresses are treated as disallowed only when explicitly checked, skipping inet6 resolution plus >allowing DNS failures creates a bypass path for destinations the policy intends to block. ></pre> > > <code>[elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex[90-100]](https://github.com/carverauto/serviceradar/blob/33c70734dd7e6b24430980586cab4b4a11b3b444/elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex/#L90-L100)</code> > <code>[elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex[102-107]](https://github.com/carverauto/serviceradar/blob/33c70734dd7e6b24430980586cab4b4a11b3b444/elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex/#L102-L107)</code> ></details> > <details> ><summary>Agent prompt</summary> ><br/> > >``` >The issue below was found during a code review. Follow the provided context and guidance below and implement a solution > >## Issue description >`OutboundURLPolicy` currently allows URLs when DNS resolution fails and only checks IPv4 resolution, enabling bypass of SSRF protections (notably for IPv6-only hosts). >### Issue Context >This validator is used for OIDC discovery/JWKS, SAML metadata fetches, and gateway JWKS fetches. >### Fix Focus Areas >- elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex[80-100] >- elixir/web-ng/test/phoenix/auth/outbound_url_policy_test.exs[1-25] >### Suggested change >- Change `resolve_and_validate/1` to return `{:error, :disallowed_host}` (or `{:error, :invalid_url}`) on DNS failures instead of `:ok`. >- Add an `:inet6` lookup and either: > - reject any IPv6 results by default (simplest/most conservative), or > - implement explicit IPv6 private/loopback/link-local/ULA checks and reject those. >- Extend tests to cover IPv6 literals and/or IPv6-only host resolution behavior where feasible. >``` > <code>ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools</code> ></details> <hr/> </details> <img src="https://www.qodo.ai/wp-content/uploads/2025/11/light-grey-line.svg" height="10%" alt="Grey Divider"> <pre>ⓘ The new review experience is currently in Beta. <a href="https://docs.qodo.ai/qodo-documentation/code-review">Learn more</a></pre> <img src="https://www.qodo.ai/wp-content/uploads/2025/11/light-grey-line.svg" height="10%" alt="Grey Divider"> <a href="https://www.qodo.ai"><img src="https://www.qodo.ai/wp-content/uploads/2025/03/qodo-logo.svg" width="80" alt="Qodo Logo"></a>
qodo-code-review[bot] commented 2026-03-02 22:09:48 +00:00 (Migrated from github.com)
Author
Owner

Imported GitHub PR review comment.

Original author: @qodo-code-review[bot]
Original URL: https://github.com/carverauto/serviceradar/pull/2981#discussion_r2874914452
Original created: 2026-03-02T22:09:48Z
Original path: elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex
Original line: 104

Action required

1. Saml datetime tuple mismatch 🐞 Bug ✓ Correctness

SAMLAssertionValidator.parse_dt/1 returns the 3-tuple from DateTime.from_iso8601/1, but
validate_time_window/3 matches only a 2-tuple, causing otherwise-valid assertions to fail with
:invalid_assertion_time (breaking SAML authentication). The new saml_assertion_validator_test.exs
"accepts valid assertion" test should fail under this implementation.
Agent Prompt
### Issue description
`ServiceRadarWebNGWeb.Auth.SAMLAssertionValidator` rejects valid assertions because it pattern-matches `DateTime.from_iso8601/1` results incorrectly.

### Issue Context
`DateTime.from_iso8601/1` is used elsewhere in the repo as a 3-tuple `{:ok, dt, offset}`; the validator currently expects `{:ok, dt}`.

### Fix Focus Areas
- elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex[21-49]
- elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex[100-105]

### Suggested change
- Implement `parse_dt/1` like:
  - `case DateTime.from_iso8601(value) do {:ok, dt, _} -> {:ok, dt}; _ -> :error end`
  - (Alternatively) change the `case` in `validate_time_window/3` to match `{{:ok, nb, _}, {:ok, noa, _}}`.
- Ensure existing/new tests in `test/phoenix/auth/saml_assertion_validator_test.exs` pass.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Imported GitHub PR review comment. Original author: @qodo-code-review[bot] Original URL: https://github.com/carverauto/serviceradar/pull/2981#discussion_r2874914452 Original created: 2026-03-02T22:09:48Z Original path: elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex Original line: 104 --- <img src="https://www.qodo.ai/wp-content/uploads/2025/12/v2-action-required.svg" height="20" alt="Action required"> 1\. Saml datetime tuple mismatch <code>🐞 Bug</code> <code>✓ Correctness</code> <pre> SAMLAssertionValidator.parse_dt/1 returns the 3-tuple from DateTime.from_iso8601/1, but validate_time_window/3 matches only a 2-tuple, causing otherwise-valid assertions to fail with :invalid_assertion_time (breaking SAML authentication). The new saml_assertion_validator_test.exs &quot;accepts valid assertion&quot; test should fail under this implementation. </pre> <details> <summary><strong>Agent Prompt</strong></summary> ``` ### Issue description `ServiceRadarWebNGWeb.Auth.SAMLAssertionValidator` rejects valid assertions because it pattern-matches `DateTime.from_iso8601/1` results incorrectly. ### Issue Context `DateTime.from_iso8601/1` is used elsewhere in the repo as a 3-tuple `{:ok, dt, offset}`; the validator currently expects `{:ok, dt}`. ### Fix Focus Areas - elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex[21-49] - elixir/web-ng/lib/serviceradar_web_ng_web/auth/saml_assertion_validator.ex[100-105] ### Suggested change - Implement `parse_dt/1` like: - `case DateTime.from_iso8601(value) do {:ok, dt, _} -> {:ok, dt}; _ -> :error end` - (Alternatively) change the `case` in `validate_time_window/3` to match `{{:ok, nb, _}, {:ok, noa, _}}`. - Ensure existing/new tests in `test/phoenix/auth/saml_assertion_validator_test.exs` pass. ``` <code>ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools</code> </details>
qodo-code-review[bot] commented 2026-03-02 22:09:48 +00:00 (Migrated from github.com)
Author
Owner

Imported GitHub PR review comment.

Original author: @qodo-code-review[bot]
Original URL: https://github.com/carverauto/serviceradar/pull/2981#discussion_r2874914458
Original created: 2026-03-02T22:09:48Z
Original path: elixir/web-ng/lib/serviceradar_web_ng_web/router.ex
Original line: 13

Action required

2. Csp blocks existing inline js 🐞 Bug ⛯ Reliability

The CSP change removes 'unsafe-inline' from script-src, but root.html.heex still includes an inline
<script> block for theme initialization. Under the new CSP, browsers will block that inline script,
likely breaking theme initialization/toggling and potentially other expected JS behavior in the root
layout.
Agent Prompt
### Issue description
CSP no longer allows inline scripts, but the root layout still contains an inline `<script>` block. Browsers will block it under the new CSP.

### Issue Context
The CSP is set for the `:browser` and `:browser_raw_auth` pipelines via `put_secure_browser_headers`.

### Fix Focus Areas
- elixir/web-ng/lib/serviceradar_web_ng_web/router.ex[11-31]
- elixir/web-ng/lib/serviceradar_web_ng_web/components/layouts/root.html.heex[14-34]
- elixir/web-ng/assets/js/app.js[1-200]

### Suggested change
- Preferred: move the theme initialization IIFE from `root.html.heex` into `assets/js/app.js` (or another bundled asset) and remove the inline `<script>` block.
- Alternative: implement per-request CSP nonces (or script hashes) and apply them to the inline `<script>` tag plus `script-src` directive.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Imported GitHub PR review comment. Original author: @qodo-code-review[bot] Original URL: https://github.com/carverauto/serviceradar/pull/2981#discussion_r2874914458 Original created: 2026-03-02T22:09:48Z Original path: elixir/web-ng/lib/serviceradar_web_ng_web/router.ex Original line: 13 --- <img src="https://www.qodo.ai/wp-content/uploads/2025/12/v2-action-required.svg" height="20" alt="Action required"> 2\. Csp blocks existing inline js <code>🐞 Bug</code> <code>⛯ Reliability</code> <pre> The CSP change removes &#x27;unsafe-inline&#x27; from script-src, but root.html.heex still includes an inline &lt;script&gt; block for theme initialization. Under the new CSP, browsers will block that inline script, likely breaking theme initialization/toggling and potentially other expected JS behavior in the root layout. </pre> <details> <summary><strong>Agent Prompt</strong></summary> ``` ### Issue description CSP no longer allows inline scripts, but the root layout still contains an inline `<script>` block. Browsers will block it under the new CSP. ### Issue Context The CSP is set for the `:browser` and `:browser_raw_auth` pipelines via `put_secure_browser_headers`. ### Fix Focus Areas - elixir/web-ng/lib/serviceradar_web_ng_web/router.ex[11-31] - elixir/web-ng/lib/serviceradar_web_ng_web/components/layouts/root.html.heex[14-34] - elixir/web-ng/assets/js/app.js[1-200] ### Suggested change - Preferred: move the theme initialization IIFE from `root.html.heex` into `assets/js/app.js` (or another bundled asset) and remove the inline `<script>` block. - Alternative: implement per-request CSP nonces (or script hashes) and apply them to the inline `<script>` tag plus `script-src` directive. ``` <code>ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools</code> </details>
qodo-code-review[bot] commented 2026-03-02 22:09:48 +00:00 (Migrated from github.com)
Author
Owner

Imported GitHub PR review comment.

Original author: @qodo-code-review[bot]
Original URL: https://github.com/carverauto/serviceradar/pull/2981#discussion_r2874914462
Original created: 2026-03-02T22:09:48Z
Original path: elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex
Original line: 109

Action required

3. Url policy dns/ipv6 bypass 🐞 Bug ⛨ Security

OutboundURLPolicy only resolves IPv4 addresses (:inet) and explicitly returns :ok when DNS
resolution fails. This can allow IPv6-only hosts (including private/loopback) or
temporarily-unresolvable hosts to pass validation, undermining SSRF protections for OIDC/SAML/JWKS
fetches.
Agent Prompt
### Issue description
`OutboundURLPolicy` currently allows URLs when DNS resolution fails and only checks IPv4 resolution, enabling bypass of SSRF protections (notably for IPv6-only hosts).

### Issue Context
This validator is used for OIDC discovery/JWKS, SAML metadata fetches, and gateway JWKS fetches.

### Fix Focus Areas
- elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex[80-100]
- elixir/web-ng/test/phoenix/auth/outbound_url_policy_test.exs[1-25]

### Suggested change
- Change `resolve_and_validate/1` to return `{:error, :disallowed_host}` (or `{:error, :invalid_url}`) on DNS failures instead of `:ok`.
- Add an `:inet6` lookup and either:
  - reject any IPv6 results by default (simplest/most conservative), or
  - implement explicit IPv6 private/loopback/link-local/ULA checks and reject those.
- Extend tests to cover IPv6 literals and/or IPv6-only host resolution behavior where feasible.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Imported GitHub PR review comment. Original author: @qodo-code-review[bot] Original URL: https://github.com/carverauto/serviceradar/pull/2981#discussion_r2874914462 Original created: 2026-03-02T22:09:48Z Original path: elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex Original line: 109 --- <img src="https://www.qodo.ai/wp-content/uploads/2025/12/v2-action-required.svg" height="20" alt="Action required"> 3\. Url policy dns/ipv6 bypass <code>🐞 Bug</code> <code>⛨ Security</code> <pre> OutboundURLPolicy only resolves IPv4 addresses (:inet) and explicitly returns :ok when DNS resolution fails. This can allow IPv6-only hosts (including private/loopback) or temporarily-unresolvable hosts to pass validation, undermining SSRF protections for OIDC/SAML/JWKS fetches. </pre> <details> <summary><strong>Agent Prompt</strong></summary> ``` ### Issue description `OutboundURLPolicy` currently allows URLs when DNS resolution fails and only checks IPv4 resolution, enabling bypass of SSRF protections (notably for IPv6-only hosts). ### Issue Context This validator is used for OIDC discovery/JWKS, SAML metadata fetches, and gateway JWKS fetches. ### Fix Focus Areas - elixir/web-ng/lib/serviceradar_web_ng_web/auth/outbound_url_policy.ex[80-100] - elixir/web-ng/test/phoenix/auth/outbound_url_policy_test.exs[1-25] ### Suggested change - Change `resolve_and_validate/1` to return `{:error, :disallowed_host}` (or `{:error, :invalid_url}`) on DNS failures instead of `:ok`. - Add an `:inet6` lookup and either: - reject any IPv6 results by default (simplest/most conservative), or - implement explicit IPv6 private/loopback/link-local/ULA checks and reject those. - Extend tests to cover IPv6 literals and/or IPv6-only host resolution behavior where feasible. ``` <code>ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools</code> </details>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
carverauto/serviceradar!3007
No description provided.