Skip to content
Draft
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ All notable changes to OriginWeave are documented in this file. The format follo
- Refreshed the product-gap queue to 126 open pull requests (54 ready, 72 draft) after #190, #188, #185, #192, #182, #184, #115, #181, #116, #117, #118, #183, #114, #127, #112, #109, #186, #110, #108, #111, #174, and #113 were merged into their immediate stacked prerequisites. PRs #147, #146, #145, #144, #143, #142, #141, #139, #136, #132, #129, and #128 moved to ready after exact-head checks and thread review; these are queue-consolidation results, not protected-main shipment.

### Added
- Added a first-GREEN characterization regression for IPv6 loopback explicit HTTP default-port receipt canonicalization and equality with the implicit-port origin; production origin parsing behavior is unchanged.
- Added regression tests for exact accepted DNS-label, DNS-host, and origin-port maxima, retaining adjacent over-limit rejection; production origin parsing behavior is unchanged.
- Corrected the 2026-08-26 product-gap snapshot with current #229 presentation-identity evidence, stacked-only #205 integration evidence, current base/head pairs, the 126-PR queue count, explicit root-versus-child merge ordering, and the active GitHub counted-approval gate.
- Refreshed the product and technical gap baseline onto the 2026-08-26 live inventory: 126 open pull requests (54 ready, 72 draft), protected-main promotion of #168/#194/#196/#216/#151, a verified maintenance-loop record (supersession closure of #153, conflict reconciliations on #37/#149/#152/#173/#175, issue #212 option-(b) authorization on #43, Strix vuln-0001 homoglyph remediation on #124), provider-rerun outcome evidence, an organization review-pipeline congestion record, and refreshed merge-order queue guidance. Documentation evidence contracts were aligned to the same snapshot so the baseline, its dated markers, and the pinned exact-head rows cannot silently diverge.

Expand Down
11 changes: 11 additions & 0 deletions crates/originweave-core/tests/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@ fn origin_accepts_secure_and_loopback_origins() {
);
}

/// Explicit IPv6 loopback HTTP port 80 must not create a distinct origin receipt.
#[test]
fn origin_omits_explicit_ipv6_default_port_from_canonical_receipt() -> Result<(), OriginError> {
let explicit_default = Origin::parse("http://[::1]:80")?;
let implicit_default = Origin::parse("http://[::1]")?;

assert_eq!(explicit_default.as_str(), "http://[::1]");
assert_eq!(explicit_default, implicit_default);
Ok(())
}

#[test]
fn origin_rejects_ambiguous_or_insecure_remote_inputs() {
let cases = [
Expand Down
37 changes: 37 additions & 0 deletions crates/originweave-core/tests/coverage_edges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,33 @@ fn origin_rejects_authorities_longer_than_the_dns_limit() {
);
}

#[test]
fn origin_accepts_exact_dns_label_and_authority_limits() {
let max_label = "a".repeat(63);
let with_max_label = format!("https://{max_label}.example");
let origin = Origin::parse(&with_max_label).expect("63-byte DNS label is the accepted maximum");
assert_eq!(origin.host(), format!("{max_label}.example"));

let labels = [63_usize, 63, 63, 61].map(|length| "a".repeat(length));
let max_host = labels.join(".");
assert_eq!(max_host.len(), 253);
Origin::parse(&format!("https://{max_host}")).expect("253-byte authority is accepted");

let oversized_label = "a".repeat(64);
assert_eq!(
Origin::parse(&format!("https://{oversized_label}.example")),
Err(OriginError::InvalidAuthority)
);

let overlong_labels = [63_usize, 63, 63, 62].map(|length| "a".repeat(length));
let overlong_host = overlong_labels.join(".");
assert_eq!(overlong_host.len(), 254);
assert_eq!(
Origin::parse(&format!("https://{overlong_host}")),
Err(OriginError::InvalidAuthority)
);
}

#[test]
fn origin_rejects_a_leading_empty_dns_label() {
assert_eq!(
Expand All @@ -40,3 +67,13 @@ fn origin_rejects_an_invalid_port_after_ipv6() {
Err(OriginError::InvalidPort)
);
}

#[test]
fn origin_accepts_the_maximum_authority_port() {
let origin = Origin::parse("https://example.com:65535").expect("65535 is a valid port");
assert_eq!(origin.as_str(), "https://example.com:65535");

let ipv6 = Origin::parse("https://[2001:db8::1]:65535")
.expect("65535 is valid for an IPv6 authority too");
assert_eq!(ipv6.as_str(), "https://[2001:db8::1]:65535");
}
2 changes: 2 additions & 0 deletions docs/TEST_STRATEGY.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ Include:
- private/link-local/metadata/platform/reserved/documentation/benchmark ranges;
- empty/oversized resolver answer;
- hostname/IP/localhost origin constraints;
- exact accepted 63-byte DNS labels, 253-byte DNS hosts, and port 65535 on DNS and IPv6 authorities, paired with over-limit rejection; these regressions lock existing parser behavior without granting destination or network authority;
- explicit IPv6 loopback HTTP default-port receipts (`http://[::1]:80` canonicalizes to `http://[::1]` and equals the implicit-port origin); this first-GREEN characterization preserves existing behavior, not a production repair or destination/network authorization;
- DNS contraction vs expansion/rebinding;
- redirect downgrade, cycle, hop limit and new destination authorization.

Expand Down
Loading