Skip to content

feat(configs): add address validation for QUIC/HTTP builders#3152

Open
Standing-Man wants to merge 7 commits intoapache:masterfrom
Standing-Man:address-validation
Open

feat(configs): add address validation for QUIC/HTTP builders#3152
Standing-Man wants to merge 7 commits intoapache:masterfrom
Standing-Man:address-validation

Conversation

@Standing-Man
Copy link
Copy Markdown
Contributor

@Standing-Man Standing-Man commented Apr 22, 2026

Which issue does this PR close?

Closes #3075.

Rationale

To align with the build methods in the tcp/websocket builders, add validation for server_address and api_url in QuicClientConfigBuilder::build() and HttpClientConfigBuilder::build(), respectively.

What changed?

In QuicClientConfigBuilder::build(), server_address is validated using validate_server_address.

In HttpClientConfigBuilder::build(), validation is performed in three steps:

  1. Ensure the api_url can be successfully parsed by Url crate
  2. If a scheme is present, restrict it to http or https
  3. Ensure the parsed api_url contains only the host and port components in its URI

Local Execution

  • Passed / not passed
    Passed
  • Pre-commit hooks ran / not ran
    Ran

AI Usage

@Standing-Man Standing-Man changed the title feat(config): add address validation for QUIC/HTTP builders feat(configs): add address validation for QUIC/HTTP builders Apr 22, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 22, 2026

Codecov Report

❌ Patch coverage is 87.50000% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.49%. Comparing base (9a9254a) to head (f995144).

Files with missing lines Patch % Lines
...guration/quic_config/quic_client_config_builder.rs 0.00% 4 Missing ⚠️
core/sdk/src/clients/client_builder.rs 0.00% 1 Missing and 1 partial ⚠️
...guration/http_config/http_client_config_builder.rs 75.00% 0 Missing and 1 partial ⚠️
core/sdk/src/http/http_client.rs 0.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #3152      +/-   ##
============================================
- Coverage     74.09%   71.49%   -2.60%     
  Complexity      943      943              
============================================
  Files          1159     1159              
  Lines        102033    97412    -4621     
  Branches      79083    74480    -4603     
============================================
- Hits          75597    69644    -5953     
- Misses        23768    24902    +1134     
- Partials       2668     2866     +198     
Components Coverage Δ
Rust Core 71.99% <87.50%> (-3.33%) ⬇️
Java SDK 60.14% <ø> (ø)
C# SDK 69.09% <ø> (-0.29%) ⬇️
Python SDK 81.43% <ø> (ø)
Node SDK 91.40% <ø> (-0.13%) ⬇️
Go SDK 39.43% <ø> (ø)
Files with missing lines Coverage Δ
core/common/src/error/iggy_error.rs 100.00% <ø> (ø)
core/common/src/utils/net.rs 99.59% <100.00%> (+0.11%) ⬆️
...guration/http_config/http_client_config_builder.rs 60.00% <75.00%> (-1.12%) ⬇️
core/sdk/src/http/http_client.rs 91.77% <0.00%> (-0.15%) ⬇️
core/sdk/src/clients/client_builder.rs 59.88% <0.00%> (ø)
...guration/quic_config/quic_client_config_builder.rs 0.00% <0.00%> (ø)

... and 114 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@hubcio
Copy link
Copy Markdown
Contributor

hubcio commented Apr 22, 2026

looks good, but next time please comment under the issue before implementation and wait till we give a green light.

Signed-off-by: StandingMan <jmtangcs@gmail.com>
@Standing-Man Standing-Man marked this pull request as ready for review April 22, 2026 13:34
@Standing-Man
Copy link
Copy Markdown
Contributor Author

Standing-Man commented Apr 22, 2026

Hi @hubcio, @spetz, @numinnex and @mmodzelewski, just a gentle ping — this PR is ready on my side. Could you please take a look when convenient?

_ => return Err(IggyError::InvalidApiUrl(addr.to_string())),
}

if api_url.host_str().is_none() || api_url.port().is_none() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

port().is_none() rejects valid urls with default-scheme ports. the url crate normalizes default ports during parse - Url::parse("http://example.com:80").port() returns None (per https://docs.rs/url/2.5.8/url/struct.Url.html#method.port: "default port numbers are never reflected by the serialization"). same for https://...:443. fix: switch to port_or_known_default().is_none() after the scheme allowlist. tests below don't cover :80 or :443 - please add them.

|| api_url.query().is_some()
|| api_url.fragment().is_some()
{
return Err(IggyError::InvalidApiUrl(addr.to_string()));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

port 0 inconsistency with validate_server_address. http://host:0 parses to Some(0) and passes here, but validate_server_address rejects port 0 (line 79, and [::1]:0 test at line 225). either both reject or both accept - align the policy.

Comment thread core/common/Cargo.toml
once_cell = { workspace = true }
papaya = { workspace = true }
rcgen = { workspace = true }
reqwest = { workspace = true }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

pulling full reqwest into the foundation crate just to use reqwest::Url. reqwest's lib.rs is pub use url::Url;, and the workspace already declares url = "2.5.8" in root Cargo.toml. swap this to url = { workspace = true } and import use url::Url; in net.rs:21. avoids dragging hyper / tokio-tls / h2 transitively into every downstream consumer of iggy_common.

return Err(IggyError::CannotParseUrl);
}
let api_url = api_url.unwrap();
let api_url = Url::parse(&config.api_url).map_err(|_| IggyError::CannotParseUrl)?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

validate_api_url only runs via HttpClientConfigBuilder::build. HttpClient::new, HttpClient::create, and HttpClient::from_connection_string all skip it - only Url::parse runs. direct HttpClientConfig { api_url: ..., ..Default::default() } literal also bypasses. validation contract leaks. move validate_api_url(&config.api_url)? here in create (the real construction boundary) so every entry path enforces it. same gap on quic side via QuicClient::create.

InvalidIpAddress(String, String) = 35,
#[error("Http error {0}")]
HttpError(String) = 36,
#[error("Invalid Api Url: {0}")]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

"Invalid Api Url: {0}" doesn't match the acronym style used at line 86 ("Invalid IP address: {0}:{1}"). prefer "Invalid API URL: {0}".

validate_api_url(&self.config.api_url)?;

Ok(self.config)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

no tests for this build(). tcp builder has a full table at tcp_client_config_builder.rs:124-189. add at minimum: default-builds-ok, trim semantics (e.g. " http://x:1 "), and one error case via invalid api_url. same gap in quic builder.

validate_server_address(&self.config.server_address)?;

Ok(self.config)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

no tests for this build(). mirror the table from tcp_client_config_builder.rs:124-189 - default-builds-ok, trim semantics, and one error case via invalid server_address.

pub fn build(self) -> QuicClientConfig {
self.config
pub fn build(mut self) -> Result<QuicClientConfig, IggyError> {
self.config.server_address = self.config.server_address.trim().to_owned();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

uses .to_owned() while tcp/websocket builders use .to_string() (tcp_client_config_builder.rs:106, websocket_client_config_builder.rs:142). functionally identical for &str, but pick one across all four builders. http builder also uses .to_owned() at line 56.

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.

Address validation for QuicClientConfigBuilder and HttpClientConfigBuilder

2 participants