Skip to content

fix(client): security hardening - #709

Open
armando-rodriguez-cko wants to merge 1 commit into
masterfrom
fix/INT-1777-security-hardening
Open

armando-rodriguez-cko wants to merge 1 commit into
masterfrom
fix/INT-1777-security-hardening

Conversation

@armando-rodriguez-cko

Copy link
Copy Markdown
Contributor

Summary

Security hardening of request building and the HTTP transport.

Changes

  • Path parameters containing /, \ or ASCII control characters are now rejected with a CheckoutArgumentException before any request is sent.
  • The base URI must be an absolute http or https URI with a host.
  • Stricter checks on the final request path and query.
  • Valid ids are unchanged on the wire.

API Reference

None.

Breaking changes

None. Values that could not reach a valid endpoint are now rejected before the request is sent.

Tests

Unit tests for path building, base URI validation and the request URL.

Related

Reported internally.

Follow-up hardening in request building and the HTTP transport. Reported internally.
@armando-rodriguez-cko
armando-rodriguez-cko requested a review from a team November 13, 2026 13:00
@agent-wall-e

agent-wall-e Bot commented Nov 13, 2026

Copy link
Copy Markdown

🟡 Risk Classification: MINOR

Approval route: AI Review + Human Approval
Rollback controls: Staged rollout + rollback

Classification reasons

  • no_low_class_matched
  • prod_source_modified

Operational gates

  • ✅ jira_ticket (INT-1777)
  • ✅ independent_review

Files analysed: 4


wall-e 2026.06.19-02 · policy 6b4ce2b3b45a…

@agent-wall-e

agent-wall-e Bot commented Nov 13, 2026

Copy link
Copy Markdown
🔬 Debug — why this classification?

Each reason code emitted by the classifier, its source clause in the AI in SDLC Control Framework, and what it means.

Reason code Kind Clause Meaning
no_low_class_matched informational §2.2 (fall-through) None of the deterministic Low classes (§2.2.3, §2.2.4, §2.2.7, docs-only) applied; classifier fell through to LLM evaluation.
prod_source_modified informational §2.1 M7 (informational) At least one file is non-doc, non-test, non-IaC — i.e. application source code was modified.

Kinds:

  • classifying — this rule contributed to the chosen tier.
  • informational — context only; did not by itself decide the tier.

See issue #3 for the proposal to formalise this map as Appendix A of the standards doc.

wall-e 2026.06.19-02 · debug

@agent-wall-e

agent-wall-e Bot commented Nov 13, 2026

Copy link
Copy Markdown

🟢 Advisory review: Looks good to me

This PR still needs a human approval — wall-e cannot auto-approve it. For what it's worth, I read the diff and found nothing I'd block on.

This PR adds path-traversal and injection hardening by rejecting '/', '', and ASCII control characters in path parameters and by validating the base URI is an absolute http/https URI with a host. The diff matches the stated intent, the new tests cover the added rejection cases and the removed 'encoding instead of rejecting' cases, and the logic looks correct.

What I checked

  • hasForbiddenCharacter correctly checks c <= 0x1F (covers U+0000–U+001F) and c == 0x7F, matching the Javadoc claim about ASCII control characters.
  • The ENCODED_SEPARATOR_OR_CONTROL pattern '%(?:2F|5C|[01][0-9A-F]|7F)' correctly catches percent-encoded '/', '', C0 controls (00–1F), and DEL (7F) in the already-encoded path segments passed to getRequestUrl.
  • The ENCODED_QUERY pattern was tightened from '[A-Za-z0-9.~*+%=&-]+' to '(?:[A-Za-z0-9.~*+=&-]|%[0-9A-Fa-f]{2})+', which now properly requires a full two-hex-digit escape after '%' rather than allowing a bare '%'; the new test cases 'event-types?version=%zz', 'event-types?version=%2', and 'event-types?version=v2%' verify this.
  • The directoryOf helper uses a live URI.resolve("probe") call, so it matches URI.resolve's actual behavior rather than reimplementing path-directory logic, which is a sound approach.
  • normalisedBaseUri calls baseUri.normalize() on paths that are already non-empty, ensuring paths like '/a/../b/' are collapsed before the directory probe, confirmed by the new test 'shouldResolveUnderBaseUrisWithUnderscoreHostsOrUnnormalisedPaths'.
  • Previously encoding '/' and '' in path parameters (the removed test cases like 'pay_1/refunds' -> 'payments/pay_1%2Frefunds') is now a hard rejection; the PR description correctly notes these values could not reach a valid endpoint because servers typically decode %2F back to a separator, so removing the encoding path is correct.
  • The redirect test ('shouldNotSendTheAuthorizationHeaderWhenFollowingARedirect') is a new integration test that exercises CustomAwsRedirectStrategy behavior; it is a useful addition independent of the core security hardening.
  • The raw authority comparison noted in the truncated patch comment (using getRawAuthority rather than getHost) is the correct approach for handling non-DNS hostnames like 'checkout_proxy' that URI.getHost() returns null for, consistent with the new test cases.

⚠️ The diff was too large to read in full, so this review covers only part of the change.


This is not an approval. wall-e cannot auto-approve this PR — it is an opinion to help whoever does. Advisory review · us.anthropic.claude-sonnet-4-6 · wall-e 2026.06.19-02

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
D Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

private static final String INVALID_REQUEST_PATH = "Invalid request path";
private static final Pattern ENCODED_QUERY = Pattern.compile("[A-Za-z0-9._~*+%=&-]+");
private static final String INVALID_BASE_URI = "Invalid base URI: it must be an absolute http or https URI with a host";
private static final Pattern ENCODED_QUERY = Pattern.compile("(?:[A-Za-z0-9._~*+=&-]|%[0-9A-Fa-f]{2})+");
private static final String INVALID_BASE_URI = "Invalid base URI: it must be an absolute http or https URI with a host";
private static final Pattern ENCODED_QUERY = Pattern.compile("(?:[A-Za-z0-9._~*+=&-]|%[0-9A-Fa-f]{2})+");
private static final Pattern ENCODED_DOT = Pattern.compile("%2e", Pattern.CASE_INSENSITIVE);
private static final Pattern ENCODED_SEGMENT = Pattern.compile("(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+");

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants