Skip to content

fix: require HTTPS for secret-bearing requests#163

Merged
larimonious merged 2 commits into
mainfrom
fix/secret-https-policy
Jul 13, 2026
Merged

fix: require HTTPS for secret-bearing requests#163
larimonious merged 2 commits into
mainfrom
fix/secret-https-policy

Conversation

@larimonious

Copy link
Copy Markdown
Contributor

Summary

Completes the ntnt-side transport boundary for std/secrets:

  • require HTTPS whenever a std/http.fetch request contains an opaque Secret
  • permit HTTP only when APP_ENV=development and the destination is localhost or a loopback IP
  • reject private-LAN, wildcard, malformed, and non-local HTTP destinations
  • force development loopback HTTP to bypass system proxies
  • pin localhost directly to IPv4/IPv6 loopback addresses
  • preserve no-redirect behavior for every secret-bearing request
  • leave ordinary non-secret HTTP behavior unchanged

Verification

  • cargo test --all-targets
  • cargo build --profile dev-release
  • generated docs + docs validation
  • examples validation and lint
  • focused subprocess tests for production rejection, development localhost allowance, and proxy bypass
  • focused Codex security review: APPROVED, no P0-P2 findings

Notes

APP_ENV controls only this application-level plaintext development exception. Existing NTNT_ENV runtime and SSRF controls remain independent and can further restrict requests.

@greptile-apps

greptile-apps Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds HTTPS enforcement for secret-bearing std/http.fetch requests, with a narrow plaintext exception for APP_ENV=development to loopback addresses only. All other HTTP destinations with secrets are rejected via validate_secret_transport.

  • Adds validate_secret_transport that parses the URL, allows HTTPS unconditionally, allows HTTP only when APP_ENV=development and the host resolves to a loopback address, and rejects everything else with a TypeError.
  • For approved loopback HTTP in development, no_proxy() is applied to the reqwest ClientBuilder and localhost is pinned to both IPv4/IPv6 loopback via resolve_to_addrs, preventing system-proxy interception and CNAME rebinding.
  • Adds format_ssrf_error to surface a helpful clarification when the SSRF guard independently blocks a request that already passed the transport check, addressing a previously noted usability gap.

Confidence Score: 5/5

Safe to merge — the new transport guard correctly enforces HTTPS for secret-bearing requests and the loopback development exception is tightly scoped, proxy-bypassed, and DNS-pinned.

The validate_secret_transport logic is sound: HTTPS passes unconditionally, the development exception requires both APP_ENV=development and a host that resolves as loopback (127.x or ::1 — not 0.0.0.0, not localhost.), and everything else is rejected before any network I/O. The no_proxy() + resolve_to_addrs hardening for the loopback path is correctly applied only when the transport check explicitly opted in. SSRF protection remains an independent gate and its augmented error message is helpful. Tests cover production rejection, development acceptance, and proxy bypass in separate subprocesses.

No files require special attention. All five changed files are consistent with one another.

Important Files Changed

Filename Overview
src/stdlib/http.rs Core change: adds validate_secret_transport, is_loopback_url, format_ssrf_error, and no_proxy/resolve_to_addrs hardening for development loopback. Logic is correct; one cosmetic double-period in augmented SSRF messages.
src/std_secrets_tests.rs Test helper updated to call http_fetch_with_app_env(Some("development")) so existing localhost-bound unit tests still reach the local listener. Correct and straightforward.
tests/language_features_tests.rs Three new integration subprocess tests cover production rejection, development loopback acceptance, and proxy bypass. capture_http_request helper uses a 3-second poll loop. Tests are sound.
docs/STDLIB_REFERENCE.md Documentation updated to mention the HTTPS requirement and the APP_ENV=development exception. New TypeError entry added for the secret transport error.
src/stdlib/secrets.rs Doc comment updated to note HTTPS requirement and APP_ENV=development exception. No logic changes.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[http_fetch called] --> B{contains_secret?}
    B -- No --> E[validate_url_for_ssrf]
    B -- Yes --> C[validate_secret_transport]
    C --> D{scheme == https?}
    D -- Yes --> E
    D -- No --> F{APP_ENV=development\nAND loopback host?}
    F -- Yes --> G[direct_loopback_http = true]
    F -- No --> H[TypeError: require HTTPS]
    G --> E
    E --> I{SSRF passes?}
    I -- No --> J[format_ssrf_error\nadd APP_ENV hint if loopback]
    I -- Yes --> K[Build reqwest Client]
    K --> L{contains_secret?}
    L -- Yes --> M[redirect = none]
    L -- No --> N[normal redirect]
    M --> O{direct_loopback_http?}
    N --> P[Execute request]
    O -- Yes --> Q[no_proxy\nresolve localhost to 127.0.0.1/::1]
    O -- No --> P
    Q --> P
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[http_fetch called] --> B{contains_secret?}
    B -- No --> E[validate_url_for_ssrf]
    B -- Yes --> C[validate_secret_transport]
    C --> D{scheme == https?}
    D -- Yes --> E
    D -- No --> F{APP_ENV=development\nAND loopback host?}
    F -- Yes --> G[direct_loopback_http = true]
    F -- No --> H[TypeError: require HTTPS]
    G --> E
    E --> I{SSRF passes?}
    I -- No --> J[format_ssrf_error\nadd APP_ENV hint if loopback]
    I -- Yes --> K[Build reqwest Client]
    K --> L{contains_secret?}
    L -- Yes --> M[redirect = none]
    L -- No --> N[normal redirect]
    M --> O{direct_loopback_http?}
    N --> P[Execute request]
    O -- Yes --> Q[no_proxy\nresolve localhost to 127.0.0.1/::1]
    O -- No --> P
    Q --> P
Loading

Reviews (2): Last reviewed commit: "fix: clarify secret transport diagnostic..." | Re-trigger Greptile

Comment thread src/stdlib/http.rs
Comment thread src/stdlib/http.rs
@larimonious

Copy link
Copy Markdown
Contributor Author

Addressed Greptile’s summary-only P2 in 0f02dbf: when APP_ENV=development admits plaintext loopback transport but the independent NTNT SSRF policy rejects it, the returned diagnostic now explicitly explains both layers. Also clarified malformed-URL errors and the reqwest DNS override port placeholder.

@larimonious larimonious merged commit 68e0483 into main Jul 13, 2026
9 checks passed
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