Skip to content

Add URL normalization test vectors and fix no-schema URL handling#39

Open
Fivell wants to merge 3 commits intomainfrom
test/request-validator-vectors
Open

Add URL normalization test vectors and fix no-schema URL handling#39
Fivell wants to merge 3 commits intomainfrom
test/request-validator-vectors

Conversation

@Fivell
Copy link
Member

@Fivell Fivell commented Mar 10, 2026

Summary

  • Fixed no-schema URL handling (prepend http://)
  • Added 15 hardcoded cross-SDK test vectors for URL normalization

Test plan

  • All 20 tests pass

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves webhook signature validation by making URL normalization accept inputs without an explicit scheme (by prepending http://) and adds a parameterized test suite of cross-SDK URL normalization vectors to lock in expected signatures.

Changes:

  • Prepend http:// when the input URL lacks a :// scheme delimiter.
  • Add 15 URL normalization/signature “golden” vectors via a JUnit 5 parameterized test.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/main/java/com/didww/sdk/callback/RequestValidator.java Adjusts URL normalization to handle no-scheme URLs by prepending http://.
src/test/java/com/didww/sdk/callback/RequestValidatorTest.java Adds parameterized test vectors to validate URL normalization + signature results across common URL variants.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 38 to 43
if (!url.matches("^[a-zA-Z]+://.*")) {
url = "http://" + url;
}
URI uri = URI.create(url);
String scheme = uri.getScheme();
String userInfo = uri.getUserInfo() != null ? uri.getUserInfo() + "@" : "";
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normalizeUrl uses uri.getScheme() later to pick default ports and also includes it in the normalized URL. URI preserves the original scheme casing (e.g., HTTPS://... -> scheme "HTTPS"), but schemes are case-insensitive; this can cause signature mismatches and wrong default-port selection. Consider lowercasing the parsed scheme (and using the lowercased value consistently for both port selection and the returned normalized URL).

Copilot uses AI. Check for mistakes.
Comment on lines +38 to +40
if (!url.matches("^[a-zA-Z]+://.*")) {
url = "http://" + url;
}
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String#matches recompiles the regex on every call. Since normalizeUrl runs for every validation, consider avoiding regex here (e.g., a small character scan / indexOf("://")) or using a precompiled Pattern constant. If you keep regex, [A-Za-z][A-Za-z0-9+.-]*:// is a more RFC-compatible scheme pattern than ^[a-zA-Z]+://.

Copilot uses AI. Check for mistakes.
…alidator

- Lowercase the URI scheme after parsing to handle mixed-case schemes correctly
- Replace url.matches() with a precompiled static Pattern to avoid recompiling the regex on every call
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}
URI uri = URI.create(url);
String scheme = uri.getScheme();
String scheme = uri.getScheme().toLowerCase();
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uri.getScheme().toLowerCase() is locale-sensitive and can produce incorrect results under certain default locales (e.g., Turkish), which would break signature validation. Use locale-independent lowercasing (e.g., toLowerCase(Locale.ROOT)) when normalizing the scheme.

Copilot uses AI. Check for mistakes.
Comment on lines +72 to +75
static Stream<Object[]> urlNormalizationVectors() {
return Stream.of(
new Object[]{"http://foo.com/bar", "4d1ce2be656d20d064183bec2ab98a2ff3981f73"},
new Object[]{"http://foo.com:80/bar", "4d1ce2be656d20d064183bec2ab98a2ff3981f73"},
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method source currently returns Stream<Object[]>, which is less type-safe and harder to read/maintain than using JUnit 5's Stream<Arguments> (or a dedicated record/class). Switching to Arguments.of(url, expectedSignature) will improve clarity and avoids accidental argument-shape mistakes.

Copilot uses AI. Check for mistakes.
- Use toLowerCase(Locale.ROOT) for scheme normalization to avoid
  locale-dependent behavior (e.g. Turkish locale)
- Restore SCHEME_PATTERN and no-scheme URL handling
- Change Stream<Object[]> to Stream<Arguments> with Arguments.of()
  for type-safe JUnit 5 parameterized tests
- Restore URL normalization test vectors
@sonarqubecloud
Copy link

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +96 to +101
RequestValidator validator = new RequestValidator("SOMEAPIKEY");
Map<String, String> payload = new LinkedHashMap<>();
payload.put("id", "1dd7a68b-e235-402b-8912-fe73ee14243a");
payload.put("status", "completed");
payload.put("type", "orders");

Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The payload setup here is duplicated across multiple tests in this class (same keys/values with only ordering differences). Extracting a small helper (e.g., a method that returns the common payload map) would reduce repetition and make it easier to update the test data consistently if the signature inputs change again.

Copilot uses AI. Check for mistakes.
Comment on lines +42 to 48
if (!SCHEME_PATTERN.matcher(url).find()) {
url = "http://" + url;
}
URI uri = URI.create(url);
String scheme = uri.getScheme();
String scheme = uri.getScheme().toLowerCase(Locale.ROOT);
String userInfo = uri.getUserInfo() != null ? uri.getUserInfo() + "@" : "";
String host = uri.getHost();
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normalizeUrl prepends http:// when no scheme is detected, but some inputs (e.g., /path, //foo.com/bar, or other non-host relative forms) will parse with uri.getHost() == null and the method will return a normalized URL containing the literal string null as the host. Consider explicitly validating that uri.getHost() is non-null/non-empty (and optionally handling scheme-relative URLs starting with //) and returning ""/failing validation instead of producing a canonical form with a null host.

Copilot uses AI. Check for mistakes.
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.

2 participants