From a02b1d6e3a2034944ba337973bc9d0385976907c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 15 Sep 2026 08:05:22 +0900 Subject: [PATCH 01/11] test(core): reject bidi controls in benchmark identity --- .../tests/controlled_benchmark_run_context.rs | 65 +++++++++++++++---- 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/crates/originweave-core/tests/controlled_benchmark_run_context.rs b/crates/originweave-core/tests/controlled_benchmark_run_context.rs index 394569321..28534f152 100644 --- a/crates/originweave-core/tests/controlled_benchmark_run_context.rs +++ b/crates/originweave-core/tests/controlled_benchmark_run_context.rs @@ -158,19 +158,60 @@ fn control_character_in_reproducibility_context_fails_closed() { } } +#[test] +fn bidi_control_in_reproducibility_context_fails_closed() { + for hostile in [ + "runner\u{061c}suffix", + "runner\u{200e}suffix", + "runner\u{200f}suffix", + "runner\u{202a}suffix", + "runner\u{202b}suffix", + "runner\u{202c}suffix", + "runner\u{202d}suffix", + "runner\u{202e}suffix", + "runner\u{2066}suffix", + "runner\u{2067}suffix", + "runner\u{2068}suffix", + "runner\u{2069}suffix", + ] { + let mut context = run_context(); + context.reasoning_configuration = hostile; + + assert_eq!( + evaluate_controlled_benchmark_suite_for_run( + context, + context, + CONTROLLED_DETERMINISTIC_REGISTRY_VERSION, + base_profile(), + &[], + ), + Err(ControlledBenchmarkSuiteError::ControlCharacterRunContext { + field: "reasoning_configuration", + }), + "bidi formatting controls must not become benchmark evidence identity: {hostile:?}" + ); + } +} + #[test] fn visible_unicode_reproducibility_context_remains_valid() { - let mut context = run_context(); - context.reasoning_configuration = "결정적-ブラウザ-oráculo-v1"; + for visible in [ + "결정적-ブラウザ-oráculo-v1", + "محرك-מבחן-v1", + ] { + let mut context = run_context(); + context.reasoning_configuration = visible; - assert_eq!( - evaluate_controlled_benchmark_suite_for_run( - context, - context, - CONTROLLED_DETERMINISTIC_REGISTRY_VERSION, - base_profile(), - &[], - ), - Ok(BenchmarkSuiteOutcome::Inconclusive) - ); + assert_eq!( + evaluate_controlled_benchmark_suite_for_run( + context, + context, + CONTROLLED_DETERMINISTIC_REGISTRY_VERSION, + base_profile(), + &[], + ), + Ok(BenchmarkSuiteOutcome::Inconclusive), + "visible Unicode and RTL scripts remain valid without bidi controls: {visible:?}" + ); + } } From 11b2c16422481eb674a90ae61e941a5b247c4024 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 15 Sep 2026 08:07:07 +0900 Subject: [PATCH 02/11] fix(core): reject bidi controls in benchmark identity --- .../src/controlled_benchmark.rs | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/crates/originweave-core/src/controlled_benchmark.rs b/crates/originweave-core/src/controlled_benchmark.rs index f601a1f99..d801017d8 100644 --- a/crates/originweave-core/src/controlled_benchmark.rs +++ b/crates/originweave-core/src/controlled_benchmark.rs @@ -609,18 +609,20 @@ fn evaluate_valid_controlled_benchmark_case( /// Evaluate raw controlled-suite evidence only when execution context is reproducible. /// /// Every required identity in the expected and observed contexts must be nonblank, -/// free of surrounding whitespace, and free of control characters before the -/// observed context is compared byte-for-byte with the expected context. This does -/// not authenticate either context; it is a fail-closed comparison boundary for a -/// benchmark runner or durable evidence pipeline that performs that authentication. +/// free of surrounding whitespace, and free of control or Unicode bidirectional +/// formatting characters before the observed context is compared byte-for-byte with +/// the expected context. This does not authenticate either context; it is a fail-closed +/// comparison boundary for a benchmark runner or durable evidence pipeline that +/// performs that authentication. /// /// # Errors /// /// Returns [`ControlledBenchmarkSuiteError::InvalidRunContext`] for a blank /// required or observed identity, [`ControlledBenchmarkSuiteError::NonCanonicalRunContext`] /// for surrounding whitespace, [`ControlledBenchmarkSuiteError::ControlCharacterRunContext`] -/// for control characters, and [`ControlledBenchmarkSuiteError::RunContextMismatch`] -/// for the first mismatched identity. After context validation, all errors from +/// for control or Unicode bidirectional formatting characters, and +/// [`ControlledBenchmarkSuiteError::RunContextMismatch`] for the first mismatched +/// identity. After context validation, all errors from /// [`evaluate_controlled_benchmark_suite`] are preserved unchanged. pub fn evaluate_controlled_benchmark_suite_for_run( expected_context: ControlledBenchmarkRunContext<'_>, @@ -728,9 +730,10 @@ pub fn evaluate_controlled_benchmark_suite( /// Rejects benchmark-owned run identities that would make evidence boundaries ambiguous. /// -/// The controlled benchmark owns these labels, so surrounding whitespace and control -/// characters are invalid here even though browser-issued protocol identifiers are -/// preserved losslessly at their own bounded-context boundary. +/// The controlled benchmark owns these labels, so surrounding whitespace, C0/C1 +/// controls, and Unicode bidirectional formatting controls are invalid here even +/// though browser-issued protocol identifiers are preserved losslessly at their own +/// bounded-context boundary. fn validate_run_context_field( field: &'static str, value: &str, @@ -742,12 +745,28 @@ fn validate_run_context_field( if trimmed != value { return Err(ControlledBenchmarkSuiteError::NonCanonicalRunContext { field }); } - if value.chars().any(char::is_control) { + if value + .chars() + .any(|character| character.is_control() || is_bidi_control(character)) + { return Err(ControlledBenchmarkSuiteError::ControlCharacterRunContext { field }); } Ok(()) } +/// Identifies the Unicode `Bidi_Control` set without rejecting ordinary RTL scripts. +/// +/// These format characters can reorder the visual presentation of otherwise byte-exact +/// benchmark identity strings. The benchmark owns this metadata grammar, so admitting +/// them would make logs, reports, and signed-summary review ambiguous even when storage +/// preserves the original scalar sequence. +fn is_bidi_control(character: char) -> bool { + matches!( + character, + '\u{061c}' | '\u{200e}' | '\u{200f}' | '\u{202a}'..='\u{202e}' | '\u{2066}'..='\u{2069}' + ) +} + /// Verifies that a per-trial outcome counter cannot claim more observations than trials. fn validate_counter( counter: &'static str, From 9175f992c81b21b5ac80db10afb6aa94ec6b3c97 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 15 Sep 2026 08:09:14 +0900 Subject: [PATCH 03/11] docs: doctor benchmark Unicode identity boundary --- ...led-benchmark-unicode-identity-security.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 docs/doctoring/controlled-benchmark-unicode-identity-security.md diff --git a/docs/doctoring/controlled-benchmark-unicode-identity-security.md b/docs/doctoring/controlled-benchmark-unicode-identity-security.md new file mode 100644 index 000000000..92c5626ed --- /dev/null +++ b/docs/doctoring/controlled-benchmark-unicode-identity-security.md @@ -0,0 +1,50 @@ +# Controlled benchmark Unicode identity security + +Status: active-PR doctoring for #322, stacked on #237. This record does not describe protected-main shipment. + +## Problem + +`ControlledBenchmarkRunContext` carries OriginWeave-owned reproducibility identities such as source revision, Chromium revision, OS image, hardware profile, protocol-adapter set, model/provider route, reasoning configuration, fixture/corpus version, and seed-set identity. These strings are compared byte-for-byte and are expected to appear in logs, reports, retained benchmark evidence, and later signed evidence summaries. + +The #237 parent already rejects blank values, surrounding whitespace, and C0/C1 control characters. Rust `char::is_control` does not cover Unicode bidirectional formatting characters. A value containing a directional override, embedding, isolate, or mark can therefore remain byte-exact while its rendered presentation differs from logical storage order. That is an evidence-review ambiguity at an application-owned identifier boundary. + +This differs from browser-issued protocol identity. OriginWeave must preserve WebDriver BiDi addresses such as `browser.UserContext` losslessly because Browser Session does not own that external identifier grammar. The controlled benchmark does own its reproducibility-label grammar, so a narrower fail-closed admission rule is appropriate here. + +## Decision + +Reject the Unicode `Bidi_Control` set in controlled-benchmark run-context identities in addition to C0/C1 controls: + +- `U+061C` ARABIC LETTER MARK; +- `U+200E` LEFT-TO-RIGHT MARK and `U+200F` RIGHT-TO-LEFT MARK; +- `U+202A..U+202E` embedding, override, and pop-directional-formatting controls; and +- `U+2066..U+2069` isolate controls. + +Do not reject ordinary visible right-to-left scripts. The regression therefore keeps Arabic and Hebrew text admissible when no bidi formatting control is present. No Unicode normalization, confusable folding, ASCII-only restriction, or browser-protocol normalization is introduced by this slice. + +The implementation remains dependency-free: a private Rust helper matches exactly the `Bidi_Control` scalar set before run-context equality can influence suite evidence. The existing typed `ControlCharacterRunContext` failure remains the fail-closed diagnostic so this repair does not widen the public error surface unnecessarily. + +## Test-first evidence + +Parent exact: `ea92c326e2dc4e3daa869aff1266c10b05453e7d`. + +Test-first exact: `a02b1d6e3a2034944ba337973bc9d0385976907c`. The test injects all 12 `Bidi_Control` scalar values and requires `ControlCharacterRunContext`; it also adds a visible Arabic/Hebrew acceptance case. The parent source only used `char::is_control`, so the new bidi-control assertions are semantic RED by source inspection. Hosted execution was unavailable at creation time; this is not an executed RED claim. + +Production repair exact: `11b2c16422481eb674a90ae61e941a5b247c4024`. `validate_run_context_field` rejects C0/C1 controls or the exact bidi-control set before equality comparison. The production change does not alter benchmark thresholds, registry membership/versioning, browser authority, model/provider routing, evidence signing/persistence, workflows, or release authority. + +## Alternatives considered + +Rejecting all non-ASCII text was rejected because it would conflate script diversity with display-control risk and would break legitimate internationalized operator-controlled labels. Applying Unicode normalization was rejected because canonicalization would change exact evidence identity and does not by itself solve bidi formatting. Treating byte equality as sufficient was rejected because UAX #9 explicitly separates logical order from rendered order for bidirectional text; human review of evidence can therefore differ from stored scalar order. + +A broader Unicode security profile may be warranted later for externally supplied product identifiers. That is not silently introduced here: any expansion to confusable, default-ignorable, or script-restriction policy needs its own threat model, compatibility analysis, tests, and versioned contract. + +## Risks and follow-up + +The current repair prevents directional formatting controls from entering benchmark-owned run-context identities but does not authenticate those identities. The durable evidence owner must still bind them to execution artifacts. It also does not claim complete Unicode spoofing resistance; UTS #39 covers a wider space of confusable and identifier-security mechanisms. + +The parent #237 and child #322 remain active-PR evidence. Exact-head repository contracts, rustfmt, locked tests, strict Clippy, rustdoc/API docs, 100% production function/line/region/branch coverage, and required security/review workflows must execute before promotion. Runner-queued or skipped jobs are not passing evidence. + +## References + +Unicode Consortium. (2026, August 27). *Unicode security mechanisms* (Unicode Technical Standard #39, Version 18.0.0, Revision 34). https://www.unicode.org/reports/tr39/tr39-34.html + +Unicode Consortium. (2025, August 13). *Unicode bidirectional algorithm* (Unicode Standard Annex #9, Version 17.0.0, Revision 51). https://www.unicode.org/reports/tr9/tr9-51.html From 33142d96f10382dcaa6534e9d36b8d1377928273 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 15 Sep 2026 08:12:28 +0900 Subject: [PATCH 04/11] docs(core): align benchmark identity diagnostic --- crates/originweave-core/src/controlled_benchmark.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/originweave-core/src/controlled_benchmark.rs b/crates/originweave-core/src/controlled_benchmark.rs index d801017d8..245fdc9d7 100644 --- a/crates/originweave-core/src/controlled_benchmark.rs +++ b/crates/originweave-core/src/controlled_benchmark.rs @@ -442,7 +442,7 @@ pub enum ControlledBenchmarkSuiteError { /// Name of the non-canonical reproducibility-context field. field: &'static str, }, - /// A required or observed reproducibility-context field contains a control character. + /// A required or observed reproducibility-context field contains a C0/C1 control or Unicode bidirectional formatting character. ControlCharacterRunContext { /// Name of the invalid reproducibility-context field. field: &'static str, @@ -491,7 +491,7 @@ impl fmt::Display for ControlledBenchmarkSuiteError { ), Self::ControlCharacterRunContext { field } => write!( formatter, - "controlled benchmark run context field {field} contains a control character" + "controlled benchmark run context field {field} contains a C0/C1 control or Unicode bidirectional formatting character" ), Self::RunContextMismatch { field } => write!( formatter, From e0f0fc662992f47c667026c62e5bf536323a3bc8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 15 Sep 2026 08:13:06 +0900 Subject: [PATCH 05/11] test(core): pin benchmark identity diagnostic --- .../tests/controlled_benchmark_run_context.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/originweave-core/tests/controlled_benchmark_run_context.rs b/crates/originweave-core/tests/controlled_benchmark_run_context.rs index 28534f152..0e30bdcc6 100644 --- a/crates/originweave-core/tests/controlled_benchmark_run_context.rs +++ b/crates/originweave-core/tests/controlled_benchmark_run_context.rs @@ -191,6 +191,14 @@ fn bidi_control_in_reproducibility_context_fails_closed() { "bidi formatting controls must not become benchmark evidence identity: {hostile:?}" ); } + + let invalid = ControlledBenchmarkSuiteError::ControlCharacterRunContext { + field: "reasoning_configuration", + }; + assert_eq!( + invalid.to_string(), + "controlled benchmark run context field reasoning_configuration contains a C0/C1 control or Unicode bidirectional formatting character" + ); } #[test] From fcc49ab34f305ee844f596d8b4d56d1bc0e599ac Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 15 Sep 2026 09:00:53 +0900 Subject: [PATCH 06/11] docs(changelog): record benchmark bidi identity hardening --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26c7ee3f4..6d1c1a8f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,7 @@ All notable changes to OriginWeave are documented in this file. The format follo ### Security +- Active PR #322 rejects Unicode `Bidi_Control` formatting scalars in controlled-benchmark run-context evidence identities while preserving ordinary visible RTL scripts, preventing byte-exact benchmark metadata from rendering with a misleading directional order. - Explicit proxy server identifiers require ASCII decimal port tokens before numeric range parsing, preventing Rust-specific leading-plus spellings from widening proxy authority. - Raw page content cannot become a trusted instruction. - Raw secrets are rejected and secret-capable actions require an opaque broker handle. @@ -103,4 +104,4 @@ All notable changes to OriginWeave are documented in this file. The format follo - The hourly product agent has no Git metadata or repository authority. A separate post-verification publisher opens one PR and cannot approve or merge it. - The unprivileged OpenCode user is restricted to loopback egress during model execution, preventing runner-wide allow-listed endpoints from becoming direct source-exfiltration channels. -[Unreleased]: https://github.com/ContextualWisdomLab/OriginWeave/compare/main...HEAD +[Unreleased]: https://github.com/ContextualWisdomLab/OriginWeave/compare/main...HEAD \ No newline at end of file From 4c17e0b36aa627e036dcbe3d82332a7bc0cc594c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 15 Sep 2026 11:01:07 +0900 Subject: [PATCH 07/11] test(core): reject Unicode line separators in benchmark identity --- .../tests/controlled_benchmark_run_context.rs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/crates/originweave-core/tests/controlled_benchmark_run_context.rs b/crates/originweave-core/tests/controlled_benchmark_run_context.rs index 0e30bdcc6..185f116ce 100644 --- a/crates/originweave-core/tests/controlled_benchmark_run_context.rs +++ b/crates/originweave-core/tests/controlled_benchmark_run_context.rs @@ -158,6 +158,28 @@ fn control_character_in_reproducibility_context_fails_closed() { } } +#[test] +fn unicode_line_separator_in_reproducibility_context_fails_closed() { + for hostile in ["runner\u{2028}spoofed=passed", "runner\u{2029}spoofed=passed"] { + let mut context = run_context(); + context.reasoning_configuration = hostile; + + assert_eq!( + evaluate_controlled_benchmark_suite_for_run( + context, + context, + CONTROLLED_DETERMINISTIC_REGISTRY_VERSION, + base_profile(), + &[], + ), + Err(ControlledBenchmarkSuiteError::ControlCharacterRunContext { + field: "reasoning_configuration", + }), + "Unicode line/paragraph separators must not split benchmark evidence identity rendering: {hostile:?}" + ); + } +} + #[test] fn bidi_control_in_reproducibility_context_fails_closed() { for hostile in [ From c89f76c5761a46745a5d1d2176828bfd6c3c2f32 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 15 Sep 2026 11:02:13 +0900 Subject: [PATCH 08/11] fix(core): reject Unicode line separators in benchmark identity --- .../src/controlled_benchmark.rs | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/crates/originweave-core/src/controlled_benchmark.rs b/crates/originweave-core/src/controlled_benchmark.rs index 245fdc9d7..a555c1377 100644 --- a/crates/originweave-core/src/controlled_benchmark.rs +++ b/crates/originweave-core/src/controlled_benchmark.rs @@ -442,7 +442,7 @@ pub enum ControlledBenchmarkSuiteError { /// Name of the non-canonical reproducibility-context field. field: &'static str, }, - /// A required or observed reproducibility-context field contains a C0/C1 control or Unicode bidirectional formatting character. + /// A required or observed reproducibility-context field contains a C0/C1 control, Unicode line/paragraph separator, or bidirectional formatting character. ControlCharacterRunContext { /// Name of the invalid reproducibility-context field. field: &'static str, @@ -491,7 +491,7 @@ impl fmt::Display for ControlledBenchmarkSuiteError { ), Self::ControlCharacterRunContext { field } => write!( formatter, - "controlled benchmark run context field {field} contains a C0/C1 control or Unicode bidirectional formatting character" + "controlled benchmark run context field {field} contains a C0/C1 control, Unicode line/paragraph separator, or bidirectional formatting character" ), Self::RunContextMismatch { field } => write!( formatter, @@ -609,20 +609,20 @@ fn evaluate_valid_controlled_benchmark_case( /// Evaluate raw controlled-suite evidence only when execution context is reproducible. /// /// Every required identity in the expected and observed contexts must be nonblank, -/// free of surrounding whitespace, and free of control or Unicode bidirectional -/// formatting characters before the observed context is compared byte-for-byte with -/// the expected context. This does not authenticate either context; it is a fail-closed -/// comparison boundary for a benchmark runner or durable evidence pipeline that -/// performs that authentication. +/// free of surrounding whitespace, and free of C0/C1 controls, Unicode line/paragraph +/// separators, or bidirectional formatting characters before the observed context is +/// compared byte-for-byte with the expected context. This does not authenticate either +/// context; it is a fail-closed comparison boundary for a benchmark runner or durable +/// evidence pipeline that performs that authentication. /// /// # Errors /// /// Returns [`ControlledBenchmarkSuiteError::InvalidRunContext`] for a blank /// required or observed identity, [`ControlledBenchmarkSuiteError::NonCanonicalRunContext`] /// for surrounding whitespace, [`ControlledBenchmarkSuiteError::ControlCharacterRunContext`] -/// for control or Unicode bidirectional formatting characters, and -/// [`ControlledBenchmarkSuiteError::RunContextMismatch`] for the first mismatched -/// identity. After context validation, all errors from +/// for C0/C1 controls, Unicode line/paragraph separators, or bidirectional formatting +/// characters, and [`ControlledBenchmarkSuiteError::RunContextMismatch`] for the first +/// mismatched identity. After context validation, all errors from /// [`evaluate_controlled_benchmark_suite`] are preserved unchanged. pub fn evaluate_controlled_benchmark_suite_for_run( expected_context: ControlledBenchmarkRunContext<'_>, @@ -731,9 +731,9 @@ pub fn evaluate_controlled_benchmark_suite( /// Rejects benchmark-owned run identities that would make evidence boundaries ambiguous. /// /// The controlled benchmark owns these labels, so surrounding whitespace, C0/C1 -/// controls, and Unicode bidirectional formatting controls are invalid here even -/// though browser-issued protocol identifiers are preserved losslessly at their own -/// bounded-context boundary. +/// controls, Unicode line/paragraph separators, and bidirectional formatting controls +/// are invalid here even though browser-issued protocol identifiers are preserved +/// losslessly at their own bounded-context boundary. fn validate_run_context_field( field: &'static str, value: &str, @@ -745,15 +745,23 @@ fn validate_run_context_field( if trimmed != value { return Err(ControlledBenchmarkSuiteError::NonCanonicalRunContext { field }); } - if value - .chars() - .any(|character| character.is_control() || is_bidi_control(character)) - { + if value.chars().any(|character| { + character.is_control() || is_unicode_line_separator(character) || is_bidi_control(character) + }) { return Err(ControlledBenchmarkSuiteError::ControlCharacterRunContext { field }); } Ok(()) } +/// Identifies Unicode separators that create a new rendered line or paragraph. +/// +/// `char::is_control` covers General Category `Cc`, not `Zl`/`Zp`. Allowing these +/// separators inside benchmark-owned evidence identity would therefore let one +/// byte-exact identity render as multiple log or report records. +fn is_unicode_line_separator(character: char) -> bool { + matches!(character, '\u{2028}' | '\u{2029}') +} + /// Identifies the Unicode `Bidi_Control` set without rejecting ordinary RTL scripts. /// /// These format characters can reorder the visual presentation of otherwise byte-exact From 4f8bce55d2f03a73260c2629c0eca2012f79a539 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 15 Sep 2026 11:03:34 +0900 Subject: [PATCH 09/11] test(core): align rendering-control diagnostic --- .../originweave-core/tests/controlled_benchmark_run_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/originweave-core/tests/controlled_benchmark_run_context.rs b/crates/originweave-core/tests/controlled_benchmark_run_context.rs index 185f116ce..089119104 100644 --- a/crates/originweave-core/tests/controlled_benchmark_run_context.rs +++ b/crates/originweave-core/tests/controlled_benchmark_run_context.rs @@ -219,7 +219,7 @@ fn bidi_control_in_reproducibility_context_fails_closed() { }; assert_eq!( invalid.to_string(), - "controlled benchmark run context field reasoning_configuration contains a C0/C1 control or Unicode bidirectional formatting character" + "controlled benchmark run context field reasoning_configuration contains a C0/C1 control, Unicode line/paragraph separator, or bidirectional formatting character" ); } From feecd1d82fa0f76b68da28f849a0fbebd006f292 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 15 Sep 2026 11:04:16 +0900 Subject: [PATCH 10/11] docs(doctoring): trace Unicode line-separator identity boundary --- ...led-benchmark-unicode-identity-security.md | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/docs/doctoring/controlled-benchmark-unicode-identity-security.md b/docs/doctoring/controlled-benchmark-unicode-identity-security.md index 92c5626ed..1cea1fa9a 100644 --- a/docs/doctoring/controlled-benchmark-unicode-identity-security.md +++ b/docs/doctoring/controlled-benchmark-unicode-identity-security.md @@ -6,45 +6,59 @@ Status: active-PR doctoring for #322, stacked on #237. This record does not desc `ControlledBenchmarkRunContext` carries OriginWeave-owned reproducibility identities such as source revision, Chromium revision, OS image, hardware profile, protocol-adapter set, model/provider route, reasoning configuration, fixture/corpus version, and seed-set identity. These strings are compared byte-for-byte and are expected to appear in logs, reports, retained benchmark evidence, and later signed evidence summaries. -The #237 parent already rejects blank values, surrounding whitespace, and C0/C1 control characters. Rust `char::is_control` does not cover Unicode bidirectional formatting characters. A value containing a directional override, embedding, isolate, or mark can therefore remain byte-exact while its rendered presentation differs from logical storage order. That is an evidence-review ambiguity at an application-owned identifier boundary. +The #237 parent rejects blank values, surrounding whitespace, and C0/C1 control characters. The first #322 slice then closed Unicode bidirectional formatting controls. A second review of the same evidence-rendering boundary found a separate gap: Rust `char::is_control` covers General Category `Cc`, while `U+2028` LINE SEPARATOR and `U+2029` PARAGRAPH SEPARATOR are `Zl` and `Zp`. An internal LS or PS survives surrounding-whitespace checks yet can render one byte-exact identity as multiple lines or paragraphs. That can make log/report review disagree with the stored scalar sequence even without a bidi override. + +Unicode UAX #44 defines `Zl` as `U+2028 LINE SEPARATOR` only, `Zp` as `U+2029 PARAGRAPH SEPARATOR` only, and `Cc` separately as C0/C1 controls. The Unicode Standard's newline guidance states that LS and PS are unambiguous Unicode line and paragraph separators and, unlike the other newline forms it discusses, are not encoded as control codes. The application therefore cannot rely on `char::is_control` to close this rendering boundary. This differs from browser-issued protocol identity. OriginWeave must preserve WebDriver BiDi addresses such as `browser.UserContext` losslessly because Browser Session does not own that external identifier grammar. The controlled benchmark does own its reproducibility-label grammar, so a narrower fail-closed admission rule is appropriate here. ## Decision -Reject the Unicode `Bidi_Control` set in controlled-benchmark run-context identities in addition to C0/C1 controls: +Reject rendering controls that can make controlled-benchmark identity presentation ambiguous: +- C0/C1 control characters already covered by `char::is_control`; +- `U+2028` LINE SEPARATOR and `U+2029` PARAGRAPH SEPARATOR; - `U+061C` ARABIC LETTER MARK; - `U+200E` LEFT-TO-RIGHT MARK and `U+200F` RIGHT-TO-LEFT MARK; - `U+202A..U+202E` embedding, override, and pop-directional-formatting controls; and - `U+2066..U+2069` isolate controls. -Do not reject ordinary visible right-to-left scripts. The regression therefore keeps Arabic and Hebrew text admissible when no bidi formatting control is present. No Unicode normalization, confusable folding, ASCII-only restriction, or browser-protocol normalization is introduced by this slice. +Do not reject ordinary visible right-to-left scripts. The regression therefore keeps Arabic and Hebrew text admissible when no formatting control or line/paragraph separator is present. No Unicode normalization, confusable folding, ASCII-only restriction, or browser-protocol normalization is introduced by this slice. -The implementation remains dependency-free: a private Rust helper matches exactly the `Bidi_Control` scalar set before run-context equality can influence suite evidence. The existing typed `ControlCharacterRunContext` failure remains the fail-closed diagnostic so this repair does not widen the public error surface unnecessarily. +The implementation remains dependency-free. One private helper matches `U+2028`/`U+2029`; another matches the exact `Bidi_Control` scalar set. Both run before run-context equality can influence suite evidence. The existing typed `ControlCharacterRunContext` failure remains the fail-closed public diagnostic so this repair does not widen the public error surface unnecessarily. ## Test-first evidence Parent exact: `ea92c326e2dc4e3daa869aff1266c10b05453e7d`. -Test-first exact: `a02b1d6e3a2034944ba337973bc9d0385976907c`. The test injects all 12 `Bidi_Control` scalar values and requires `ControlCharacterRunContext`; it also adds a visible Arabic/Hebrew acceptance case. The parent source only used `char::is_control`, so the new bidi-control assertions are semantic RED by source inspection. Hosted execution was unavailable at creation time; this is not an executed RED claim. +Bidi test-first exact: `a02b1d6e3a2034944ba337973bc9d0385976907c`. The test injects all 12 `Bidi_Control` scalar values and requires `ControlCharacterRunContext`; it also adds a visible Arabic/Hebrew acceptance case. The parent source only used `char::is_control`, so the bidi-control assertions were semantic RED by source inspection. Hosted execution was unavailable at creation time; this is not an executed RED claim. + +Bidi production repair exact: `11b2c16422481eb674a90ae61e941a5b247c4024`. `validate_run_context_field` rejects C0/C1 controls or the exact bidi-control set before equality comparison. + +Line-separator test-first exact: `4c17e0b36aa627e036dcbe3d82332a7bc0cc594c`. It adds hostile `U+2028` and `U+2029` identities and requires the same typed fail-closed error. The predecessor source accepted both because they are not `Cc` and are outside `Bidi_Control`; this is again a semantic RED established from the exact predecessor source, not a claim that a hosted runner executed the failing test. -Production repair exact: `11b2c16422481eb674a90ae61e941a5b247c4024`. `validate_run_context_field` rejects C0/C1 controls or the exact bidi-control set before equality comparison. The production change does not alter benchmark thresholds, registry membership/versioning, browser authority, model/provider routing, evidence signing/persistence, workflows, or release authority. +Line-separator production repair exact: `c89f76c5761a46745a5d1d2176828bfd6c3c2f32`. The validator adds the narrow `is_unicode_line_separator` predicate, and rustdoc plus the public diagnostic are widened only enough to describe the actual admitted rendering-control boundary. Exact `4f8bce55d2f03a73260c2629c0eca2012f79a539` aligns the diagnostic regression with that public message. + +None of these changes alter benchmark thresholds, registry membership/versioning, browser authority, model/provider routing, evidence signing/persistence, workflows, or release authority. ## Alternatives considered -Rejecting all non-ASCII text was rejected because it would conflate script diversity with display-control risk and would break legitimate internationalized operator-controlled labels. Applying Unicode normalization was rejected because canonicalization would change exact evidence identity and does not by itself solve bidi formatting. Treating byte equality as sufficient was rejected because UAX #9 explicitly separates logical order from rendered order for bidirectional text; human review of evidence can therefore differ from stored scalar order. +Rejecting all non-ASCII text was rejected because it would conflate script diversity with rendering-control risk and would break legitimate internationalized operator-controlled labels. Applying Unicode normalization was rejected because canonicalization would change exact evidence identity and does not by itself solve directional reordering or explicit line/paragraph separation. Treating byte equality as sufficient was rejected because UAX #9 separates logical order from rendered order for bidirectional text, while the Unicode Standard explicitly assigns LS and PS line/paragraph boundary semantics. -A broader Unicode security profile may be warranted later for externally supplied product identifiers. That is not silently introduced here: any expansion to confusable, default-ignorable, or script-restriction policy needs its own threat model, compatibility analysis, tests, and versioned contract. +A broader Unicode security profile may be warranted later for externally supplied product identifiers. That is not silently introduced here: any expansion to confusable, default-ignorable, script-restriction, or normalization policy needs its own threat model, compatibility analysis, tests, and versioned contract. ## Risks and follow-up -The current repair prevents directional formatting controls from entering benchmark-owned run-context identities but does not authenticate those identities. The durable evidence owner must still bind them to execution artifacts. It also does not claim complete Unicode spoofing resistance; UTS #39 covers a wider space of confusable and identifier-security mechanisms. +The current repair prevents the reviewed directional and line/paragraph rendering controls from entering benchmark-owned run-context identities but does not authenticate those identities. The durable evidence owner must still bind them to execution artifacts. It also does not claim complete Unicode spoofing resistance; UTS #39 covers a wider space of identifier-security mechanisms. -The parent #237 and child #322 remain active-PR evidence. Exact-head repository contracts, rustfmt, locked tests, strict Clippy, rustdoc/API docs, 100% production function/line/region/branch coverage, and required security/review workflows must execute before promotion. Runner-queued or skipped jobs are not passing evidence. +The parent #237 and child #322 remain active-PR evidence. Exact-head repository contracts, rustfmt, locked tests, strict Clippy, rustdoc/API docs, 100% production function/line/region/branch coverage, and required security/review workflows must execute before promotion. Cancelled, skipped, queued-only, predecessor, or status-only jobs are not passing evidence. ## References Unicode Consortium. (2026, August 27). *Unicode security mechanisms* (Unicode Technical Standard #39, Version 18.0.0, Revision 34). https://www.unicode.org/reports/tr39/tr39-34.html Unicode Consortium. (2025, August 13). *Unicode bidirectional algorithm* (Unicode Standard Annex #9, Version 17.0.0, Revision 51). https://www.unicode.org/reports/tr9/tr9-51.html + +Unicode Consortium. (2025). *Unicode Character Database* (Unicode Standard Annex #44, Version 17.0.0). https://www.unicode.org/reports/tr44/ + +Unicode Consortium. (2025). *The Unicode Standard, Version 17.0.0: Chapter 5, Implementation guidelines—Newline guidelines*. https://www.unicode.org/versions/Unicode17.0.0/core-spec/chapter-5/ From a4c8ceaf67a075ef483334802aacfc54cf502068 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 15 Sep 2026 11:05:32 +0900 Subject: [PATCH 11/11] docs(changelog): include Unicode line-separator identity repair --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d1c1a8f8..1fad2fc6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ All notable changes to OriginWeave are documented in this file. The format follo - Added `originweave_core::release_acceptance`, a deterministic fail-closed benchmark release-decision contract that requires one authoritative result for every mandatory suite, bounds explicit buyer-visible limitations, rejects duplicate limitation claim identities, and rejects non-canonical surrounding whitespace rather than normalizing it into an alternate claim spelling. - Added active PR #237's versioned controlled-deterministic benchmark registry and raw-evidence threshold evaluator: exactly 100 clean trials are required per case, missing or inconclusive required cases cannot pass the suite, conditional cases follow the declared support profile, registry versions are exact, and impossible aggregate counters fail closed. -- Refreshed the product and technical gap baseline with the 2026-08-24 live inventory: 158 open pull requests (44 ready, 114 draft), refreshed exact base/head evidence for the #208–#222 release, enterprise-approval, BAP, and WARC/PROV chains, the governance issue additions #212 and #215, and a required-check provider-failure record for the fail-closed Strix re-dispatches on #208/#218/#220. +- Refreshed the product and technical gap baseline with the 2026-08-24 live inventory: 158 open pull requests (44 ready, 114 draft), refreshed exact base/head evidence for the #208–#222 release, enterprise-approval, BAP, WARC/PROV chains, the governance issue additions #212 and #215, and a required-check provider-failure record for the fail-closed Strix re-dispatches on #208/#218/#220. - Added a dated product and technical gap baseline that separates protected-main implementation truth, active pull-request evidence, live review/check blockers, and the next buyer-visible Phase 1 acceptance work. - Refreshed the product and technical gap baseline with the current open-PR inventory and exact base/head evidence for the newest Chromium, BAP, extraction, WARC, and idempotency slices. - Bound explicit extension-to-Agent grants to exclusive trusted-time expiry in addition to extension identity, session, browsing context, and canonical origin, so a same-origin grant cannot be reused at or after the deadline. @@ -71,7 +71,7 @@ All notable changes to OriginWeave are documented in this file. The format follo ### Security -- Active PR #322 rejects Unicode `Bidi_Control` formatting scalars in controlled-benchmark run-context evidence identities while preserving ordinary visible RTL scripts, preventing byte-exact benchmark metadata from rendering with a misleading directional order. +- Active PR #322 rejects C0/C1 controls, Unicode `U+2028` LINE SEPARATOR / `U+2029` PARAGRAPH SEPARATOR, and `Bidi_Control` formatting scalars in controlled-benchmark run-context evidence identities while preserving ordinary visible RTL scripts, preventing byte-exact benchmark metadata from rendering as a misleading direction or multiple log/report records. - Explicit proxy server identifiers require ASCII decimal port tokens before numeric range parsing, preventing Rust-specific leading-plus spellings from widening proxy authority. - Raw page content cannot become a trusted instruction. - Raw secrets are rejected and secret-capable actions require an opaque broker handle.