Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 34 additions & 23 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ git = "https://github.com/inclavare-containers/webpki.git"

[patch.crates-io.reqwest]
git = "https://github.com/inclavare-containers/reqwest.git"
rev = "edd4658e698a030ff04bbb62e954f363b31a25c8"
rev = "472703bfd5d3eb415bece730230a823e07dabb78"

[patch.crates-io.tokio-graceful]
branch = "wasm"
Expand Down
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,6 @@ wasm-pack-debug: wasm-build-debug

.PHONE: wasm-unit-test
wasm-unit-test: wasm-unit-test-chrome
RUSTUP_TOOLCHAIN=nightly-2025-07-07 RUSTFLAGS='--cfg getrandom_backend="wasm_js" -C target-feature=+atomics,+bulk-memory,+mutable-globals' wasm-pack test --headless --chrome ./tng-wasm -Z build-std=std,panic_abort

.PHONE: wasm-unit-test-chrome
wasm-unit-test-chrome: install-wasm-build-dependencies
Expand Down
6 changes: 6 additions & 0 deletions rats-cert/src/tee/coco/converter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,12 @@ impl BuiltinCocoConverter {
})
}

/// Builtin converters run the attestation service in-process and have no
/// remote attestation-service address; return a sentinel for error context.
pub fn as_addr(&self) -> &'static str {
"<builtin-attestation-service>"
}

/// Load policy from configuration
/// Returns None for the AS built-in default policy
/// (HardwareWithReferenceValues), and a URL-safe base64 encoding of the
Expand Down
5 changes: 5 additions & 0 deletions rats-cert/src/tee/coco/converter/grpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ impl CocoGrpcConverter {
request_metadata,
})
}

/// The attestation-service gRPC address this converter targets.
pub fn as_addr(&self) -> &str {
&self.as_addr
}
}

#[async_trait::async_trait]
Expand Down
12 changes: 12 additions & 0 deletions rats-cert/src/tee/coco/converter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ pub enum CocoConverter {
Builtin(BuiltinCocoConverter),
}

impl CocoConverter {
/// The attestation-service address this converter targets (for error context).
pub fn as_addr(&self) -> &str {
match self {
CocoConverter::Restful(c) => c.as_addr(),
CocoConverter::Grpc(c) => c.as_addr(),
#[cfg(feature = "__builtin-as")]
CocoConverter::Builtin(c) => c.as_addr(),
}
}
}

pub enum CoCoNonce {
Jwt(String),
}
Expand Down
5 changes: 5 additions & 0 deletions rats-cert/src/tee/coco/converter/restful.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ impl CocoRestfulConverter {
policy_ids: policy_ids.to_owned(),
})
}

/// The attestation-service base address this converter targets.
pub fn as_addr(&self) -> &str {
&self.as_addr
}
}

mod as_api {
Expand Down
5 changes: 5 additions & 0 deletions rats-cert/src/tee/ita/converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ impl ItaConverter {
})
}

/// The ITA portal base URL this converter targets.
pub fn as_addr(&self) -> &str {
&self.base_url
}

fn is_retryable_error(status: reqwest::StatusCode, body: &str) -> bool {
status.is_server_error()
|| (status == reqwest::StatusCode::BAD_REQUEST
Expand Down
3 changes: 2 additions & 1 deletion tng-wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ tokio_with_wasm = {workspace = true}
tokio_with_wasm_proc = {workspace = true}

[dev-dependencies]
wasm-bindgen-test = "0.3.34"
wasm-bindgen-test = "=0.3.50"
reqwest = {workspace = true}

# https://github.com/rustwasm/wasm-pack/issues/1351#issuecomment-2100231587
[package.metadata.wasm-pack.profile.dev.wasm-bindgen]
Expand Down
53 changes: 53 additions & 0 deletions tng-wasm/tests/fetch_error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//! Verifies that a browser fetch rejection renders cleanly (no duplicated
//! `TypeError: Failed to fetch`, no raw `JsValue(...)` wrapper) and carries the
//! browser-opacity hint. Exercises the patched reqwest fork's
//! `crate::error::wasm()` end-to-end via the real `reqwest` wasm client.

#![cfg(target_arch = "wasm32")]

extern crate wasm_bindgen_test;

use std::error::Error;
use wasm_bindgen_test::*;

// Configure tests to run in the browser environment.
wasm_bindgen_test_configure!(run_in_browser);

#[wasm_bindgen_test]
async fn fetch_failure_renders_cleanly() {
let client = reqwest::Client::builder().build().unwrap();
let err = client
.get("https://invalid.invalid/")
.send()
.await
.expect_err("fetch to an invalid host must fail");

// reqwest::Error Display is "error sending request"; its `source()` is the
// string produced by the fork's crate::error::wasm() — the link under test.
// The reqwest::Error's direct source IS the wasm() BoxError, so a single
// `.source()` call reaches it.
let source = err
.source()
.expect("reqwest error has a source")
.to_string();

assert!(
!source.contains("JsValue("),
"raw JsValue(...) wrapper leaked into the error:\n{source}"
);

// On firefox the error will be "TypeError: NetworkError when attempting to fetch resource.", not "TypeError: Failed to fetch"
if source.contains("TypeError: NetworkError when attempting to fetch resource.") {
// firefox, skip this part
} else {
let occurrences = source.matches("TypeError: Failed to fetch").count();
assert_eq!(
occurrences, 1,
"expected exactly one 'TypeError: Failed to fetch', got {occurrences}:\n{source}"
);
assert!(
source.contains("Note: browsers do not expose"),
"missing browser-opacity hint:\n{source}"
);
}
}
11 changes: 9 additions & 2 deletions tng/src/tunnel/ingress/protocol/ohttp/security/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,13 @@ impl OHttpClientInner {
let challenge_token = converter
.get_nonce()
.await
.map_err(|e| TngError::ClientRequestKeyConfigFailed(e.into()))?;
.with_context(|| {
format!(
"requesting challenge token from AS at {}",
converter.as_addr()
)
})
.map_err(TngError::ClientRequestKeyConfigFailed)?;

// Request hpke configuration for server
let response = self
Expand Down Expand Up @@ -496,7 +502,8 @@ impl OHttpClientInner {
.json(&key_config_request)
.send()
.await
.map_err(|error| TngError::ClientRequestKeyConfigFailed(error.into()))?
.with_context(|| format!("POST key-config request to {}", self.base_url))
.map_err(TngError::ClientRequestKeyConfigFailed)?
.check_error_response()
.await
.map_err(TngError::ClientRequestKeyConfigFailed)?;
Expand Down
8 changes: 8 additions & 0 deletions tng/src/tunnel/provider/converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ impl TngConverter {
Self::Ita(_) => super::provider_type::ProviderType::Ita,
}
}

/// The attestation-service address this converter targets (for error context).
pub fn as_addr(&self) -> &str {
match self {
Self::Coco(c) => c.as_addr(),
Self::Ita(c) => c.as_addr(),
}
}
}

#[async_trait::async_trait]
Expand Down
Loading