An outbound URL allowlist is effective only if it constrains the destination the application actually connects to, not merely a string it examined before making a request. To review it, follow the URL from input through parsing, approval, DNS resolution, redirects, the HTTP client, and network egress. At each step, ask whether the destination or interpretation can change after approval. If the application chooses from fixed internal destinations rather than accepting URLs, the review is smaller; if users or third parties supply full URLs, every transition matters.
This is a server-side request forgery (SSRF) review. It does not prove that a particular internal service is reachable or that a response would disclose data. A source review can show a plausible route and a missing control; actual network behavior requires deployment evidence or a separately authorized, controlled observation. OWASP's SSRF prevention guidance recommends identifying trusted destinations, validating both domain syntax and membership, and accounting for redirects and DNS behavior.
Follow one ordinary business request
Illustrative case; all names, domains, and revisions are invented. Larch Commerce imports inventory feeds. A merchant submits a feed URL; a background worker fetches it and updates product availability. The intended policy permits only feeds.partner.example over HTTPS. At revision INV-42, the route checks that the submitted text contains partner.example, then passes the original text to an HTTP client that follows redirects. A network policy allows broad outbound HTTPS because the worker also calls other services.
The reviewer should not start by collecting clever URL strings. The important observation is structural: the code compares one string, but the client decides the connection target later. The code may treat an unrelated name as approved, and a permitted first response may redirect the client elsewhere. Whether either path exists in the deployed application remains a separate question. The invented domain is not a test target.
The review artifact is a destination ledger:
| Stage | Decision or transformation | Evidence to collect | Review question |
|---|---|---|---|
| Input | Merchant supplies a URL | Route schema and actor permissions at INV-42 |
Is a complete URL accepted, or only a server-chosen key? |
| Parsing | Application extracts scheme, host and port | Parser call and error behavior | Is the same parsed object used for approval and connection? |
| Policy | Host is compared with approved destinations | Allowlist definition and matching function | Is comparison exact or an explicitly bounded subdomain rule? |
| Name resolution | Host becomes connection addresses | Resolver/client configuration | Are resolved addresses checked for prohibited ranges at connection time? |
| Redirect | Client may receive a new location | Redirect configuration | Is each new destination rejected or re-authorized? |
| Connection | Client opens socket, sends headers and reads response | HTTP client and egress policy | Can network policy stop a destination the application misses? |
| Import | Worker parses fetched content | Size, type and import controls | Could a response create a second file-processing risk? |
This ledger distinguishes an apparent code defect from a complete harmful path. For example, the merchant's permissions may limit who can submit URLs, but they do not make an arbitrary destination trusted. Conversely, a tightly scoped egress policy might block a connection even if string validation is flawed. Record both facts.
Turn the allowlist into an explicit request contract
Larch chooses a narrow contract for its fictional feed integration:
scheme: HTTPS only
host: exactly feeds.partner.example in one documented canonical form
port: 443 only
userinfo: absent
fragment: absent
redirects: disabled for the first release
path/query: only the feed resource and supported parameters for this merchant
credentials: only the integration credential selected by server configuration
These are example product decisions, not a universal URL policy. The contract must be evaluated using the parsed components the connector will use. RFC 3986 separates scheme, authority, path, query and fragment; searching the original text for a partner name cannot identify its actual host.
Review ordinary alternatives before exotic strings. A URL for another host with /partner.example/feed in its path does not qualify. A URL for the correct host on port 8443 does not qualify under this contract. A response redirecting to the partner's new CDN does not automatically qualify either. The owner can deliberately add that destination after review, or the importer can return a clear unsupported-redirect result. The fix should not silently fail open to preserve compatibility. A permitted host can expose more than the feed endpoint. If the worker attaches a privileged partner credential, restrict paths and parameters to the merchant's authorized feed so the allowlist does not become a way to invoke unrelated partner operations.
Follow one approved name through two DNS decisions
The hostname can remain unchanged while the connection destination changes. This synthetic trace uses symbolic addresses; none are network targets:
T0 validator parses feeds.partner.example:443
T1 resolver returns address A, approved by destination policy
T2 validator returns the original URL string
T3 HTTP client performs another lookup and receives address B
T4 client connects to B without the earlier policy decision
The missing edge is T3→T4. A check at T1 does not establish which peer was contacted. A reviewed connector can bind the connection to an approved address while preserving the original hostname for TLS certificate verification, SNI and HTTP host identity. OWASP's current SSRF guidance describes this requirement and extends it to fallback connections and retries. Replacing the hostname with an IP and disabling TLS verification is not an acceptable shortcut.
The owner returns a corrected design trace:
parse once → approve scheme/host/port
resolve → evaluate every candidate address
connect only using an approved address through enforced egress
verify TLS for feeds.partner.example
on retry: repeat the same destination decision; no generic fallback client
on redirect: stop under the current product policy
This is a contract to inspect, not a complete HTTP-client implementation. Proxy mode must be included: if the proxy resolves and connects, its enforced policy must carry the equivalent restriction. Observing the application's socket connected to the proxy does not by itself reveal the proxy's final destination.
Give each failure a different disposition
A parser that rejects the wrong port is positive evidence about the parsed policy. A client that follows a disallowed redirect has a request-policy defect. A valid partner request failing because its TLS certificate is invalid is an identity/availability result, not proof that the off-policy network boundary worked. A retry selecting an unchecked fallback address reopens the destination claim even if the first attempt was constrained.
The useful returned artifact names the parsed URL components, approved address set, actual connector or proxy, redirect decision and retry behavior for the selected client version. It omits credentials and private response content. Without that artifact, the review can identify the source gap but should not claim the final connection stayed within policy.
Determine whether the control is independent
The application check is one boundary. Network egress is another. For the example worker, inspect the deployed network policy, proxy rules, service identity and any service-specific exceptions. If the worker can reach every HTTPS address, the network layer contributes little to this particular restriction. If egress only permits a fixed partner address range or an authenticated proxy that enforces destinations, record its actual scope and the fact that partner addresses may change. Do not call a policy effective merely because a configuration file exists in the repository; obtain the active version and owner.
The review should also identify what a successful SSRF path could reach. Is there an internal API with the worker's credentials? Are request headers inherited by outbound calls? Are responses returned to the merchant, imported into inventory, or only stored in private object storage? The business effect changes with those facts. Avoid claiming credential disclosure solely because a destination rule is weak.
Decide and hand off a narrow finding
At INV-42, the sample evidence supports this note:
Source-supported concern: The inventory worker accepts a merchant URL. The route approves the input through a substring comparison, while the HTTP client receives the original URL and follows redirects. The application code does not establish that the final connection stays on the approved host.
Potential effect: A permitted merchant may cause the worker to contact a destination outside the feed partner policy. What it could read, send or reach is unconfirmed.
Counterevidence to check: Deployed egress/proxy policy, client configuration overrides, route availability and any downstream destination enforcement.
Owner and action: The service owner should use one parsed destination for policy and request, restrict schemes and ports, disable or revalidate redirects, and ensure connection-time/network enforcement. Security maps the selected deployment to the impact claim while the owner implements the bounded repair.
This note does not include a payload or claim a live request was made. If source and deployment evidence show the outbound path is unreachable, reject or narrow that particular claim. If an effective network boundary blocks the unintended connection but code validation remains weak, record a hardening issue separately from a confirmed reachable SSRF. If the deployment or client behavior cannot be established, keep the claim in investigation rather than promoting it to a verified vulnerability.
After a fix, compare a pinned new revision with INV-42; inspect the production client and active egress rule; and have an authorized owner observe that a synthetic off-policy destination is denied in a controlled environment while the approved partner path still works. A missing scanner alert alone is not validation. The OWASP guidance on parsed destinations in redirect handling reinforces the same principle: decide on parsed components that the application will actually use.
Make allowlist changes reviewable configuration changes
Larch's partner may legitimately move feeds to another hostname or CDN. The operational process should let an owner propose that destination, establish the required path and credential behavior, and update both application and network enforcement deliberately. A temporary fallback that follows any redirect would remove the boundary the original contract was meant to provide.
Keep the configuration identity in the evidence packet. If source at INV-42 loads a destination list from a separate service, the code revision alone does not identify the allowed set. Record who can modify the list and whether an unauthorized tenant can broaden it for every merchant. A list of hosts can be a security policy even when it is stored as ordinary configuration data.
Also review what happens when the partner stops matching the contract. A new port, redirect, or path should produce a useful bounded failure and an owner decision, not an automatic expansion. This makes availability incidents easier to resolve without quietly accepting a wider request capability.
The final change note should state the new permitted destination, its intended business use, the affected client/proxy configuration, and the remaining validation. That gives the next reviewer a stable baseline. “Allowlist updated” is too vague to explain whether the final connection, credential forwarding, and merchant-specific feed access still match the original requirement.
For the next review, fill the destination ledger before assigning severity. If the uncertainty is broader than the URL check, use a testable claim template. If the reported route may not run in production, continue with how to explain a source-only finding that needs runtime proof.