feat(analyzer): TLS buffer-saturation telemetry — buffer_saturation_drops counter (STORY-146)#344
Merged
Zious11 merged 8 commits intoJun 30, 2026
Conversation
…m, Red Gate) Files created: none (modified src/analyzer/tls.rs only) todo!() functions: 2 ## GREEN-BY-DESIGN none ## WIRING-EXEMPT none Field initialization `buffer_saturation_drops: 0` in `TlsAnalyzer::new()` is real (not todo!) — struct field initializers must be concrete values for the struct literal to compile; zero-initialization is not implementation logic (BC-5.38.001 non-trivial body rule does not apply to field initializers). Self-check (BC-5.38.005 invariant 1) applied to both stub bodies: buffer_saturation_drop_count: "If I include this real implementation, will the test for this function pass trivially without any implementer work?" — YES: returning self.buffer_saturation_drops directly would satisfy test_BC_2_07_043_no_drop_no_counter (drops==drops, zero==zero). Therefore: todo!(). fill_buf_for_testing: "If I include this real implementation, will the test for this function pass trivially without any implementer work?" — YES: a working fill seam enables the full-drop test fixture, allowing the (not yet wired) increment path to be exercised. Therefore: todo!(). Red Gate: 140 pre-existing tests GREEN; 0 new STORY-146 tests exist (will be written by test-writer in next step). cargo check clean; clippy -D warnings clean.
…tests, mod story_146)
…rops counter (STORY-146, VP-040)
AC-146-001: implement buffer_saturation_drop_count() accessor (self.buffer_saturation_drops);
remove temporary #[allow(dead_code)] from the field now that it is read.
AC-146-001: implement fill_buf_for_testing(&mut self, flow_key, direction, n) seam —
fills client_buf (C2S) or server_buf (S2C) to exactly n bytes via buf.clear()+resize;
creates flow state entry if absent; precondition checked via debug_assert.
AC-146-002 (borrow-constraint pattern): inside the &mut state block in on_data, compute
let remaining = MAX_BUF.saturating_sub(buf.len());
let did_drop = data.len() > remaining;
for both Direction::ClientToServer and Direction::ServerToClient arms. After the block
closes (mutable borrow on self.flows released), add:
if did_drop { self.buffer_saturation_drops += 1; }
Placement is between the buffer-append block and try_parse_records (tls.rs lines ~1149-1168).
Byte-drop semantics of BC-2.07.005 are unchanged; only telemetry is added.
AC-146-005: insert "buffer_saturation_drops" key into summarize() detail BTreeMap after
the existing handshake_reassembly_overflows entry; key always present even at 0 (EC-008).
Update test_summarize_output: key count 8→9 and required_keys list extended to include
"buffer_saturation_drops" (mirrors STORY-144's 7→8 update for handshake_reassembly_overflows).
All 6 VP-040 Red-Gate tests pass; full suite 146 tests green; clippy -D warnings clean;
cargo fmt --check clean.
…46-001/002) F-146-001 (src/analyzer/tls.rs ~1430): replaced the RED-phase self-check rationale block whose operative conclusion was `todo!()` for `buffer_saturation_drop_count` and `fill_buf_for_testing`. Rewrote as past-tense historical note describing what was done during the RED gate and what each function now does. F-146-002 (tests/tls_analyzer_tests.rs ~10970): replaced "Red-Gate status: all 6 MUST FAIL before implementation" / "will panic on `todo!()`" header with present-tense "GREEN status: all 6 tests pass" describing what each test now exercises. Mandatory DF-GREEN-DOC-TENSE-SWEEP: grep confirms zero stale tokens remain in STORY-146 additions. Remaining hits are pre-existing STORY-144/145 prose outside this story's diff (tls.rs:1381, tests:435/9537/9539/9576/10455/10530/10717) — out of scope per sweep rules. All tests: green (96+4+3+22+…). Clippy: clean. Fmt: clean.
…/EC-C3 (F-146-01/02) F-146-01: Change `buffer_saturation_drops += 1` to `buffer_saturation_drops.saturating_add(1)` (src/analyzer/tls.rs:1171), matching the exact form used by the three sibling `handshake_reassembly_overflows` increments. Adds an inline comment citing SEC-003 / sibling consistency. Avoids theoretical overflow-check panic under the release profile's `overflow-checks = true`. Behavior identical for all realistic inputs. F-146-02: Add two regression tests inside `mod story_146` (tests/tls_analyzer_tests.rs, DF-TEST-NAMESPACE-001): - test_BC_2_07_043_partial_drop_boundary (EC-C1): fill to 65,535 bytes, deliver 2 bytes → drops_before+1, parse_errors unchanged. Would fail if drop detection were broken. - test_BC_2_07_043_full_buffer_empty_data_no_count (EC-C3): fill to MAX_BUF, deliver &[] → drops_before (no increment). Would fail under a >= mutation. tls_analyzer_tests: 146 → 148. All checks green (test/clippy/fmt/green-doc-tense).
… docstring (OBS-146-01) The old docstring claimed EC-C1 distinguishes the `to_copy < data.len()` mutant form. That claim is wrong: at EC-C1 (remaining=1, data=2 bytes), to_copy=min(2,1)=1, so `1 < 2` is true and the mutant still increments — the test still passes. EC-C1 does not kill that mutant. Rewritten to state what EC-C1 actually pins: the partial-drop boundary counter fires and parse_errors are unchanged. Notes that the `to_copy < data.len()` mutant is killed by the full-drop tests (remaining==0), not by this test. No logic or assertion changes. Comment/docstring only.
…sibling-parity (F-146-ADV-01 + LOW) F-146-ADV-01 (MEDIUM): Update mod story_146 header comments from "ALL 6 new test functions" and "all 6 tests pass" to "ALL 8 test functions (6 canonical VP-040 Red-Gate + 2 EC-coverage)" and "all 8 tests pass". Sibling-sweep confirmed only two stale occurrences (lines 10964, 10970); zero remain. LOW-1: Remove #[doc(hidden)] from buffer_saturation_drop_count() so it matches the plain `pub fn` pattern of its siblings truncated_record_count() (line 411) and handshake_reassembly_overflow_count() (line 1430). The test seam fill_buf_for_testing() retains #[doc(hidden)] as intended. LOW-2: Update non-tautology doc-comment phrasing from `buffer_saturation_drops += 1` to `buffer_saturation_drops.saturating_add(1)` to match the actual implementation at tls.rs:1171. Comment/annotation changes only. All 2220 tests green. Clippy clean. Fmt clean. check-green-doc-tense PASS.
Owner
Author
Review Cycle 1 Triage
pr-reviewer verdict: APPROVE — 0 blocking, 0 non-blocking, 0 cosmetic findings. Diff is tight, internally consistent, test-backed, well-commented, and accurately described in the PR body. security-reviewer verdict: CLEAR — 0 CRITICAL, 0 HIGH, 0 MEDIUM, 0 LOW. All 5 security assertions confirmed safe: no new DoS surface, saturating_add overflow-safe (CWE-190 n/a), fill_buf_for_testing not reachable from production, byte-drop semantics unchanged, no injection in summarize() key. Convergence: CONVERGED in 1 cycle. Ready to merge pending CI green. Human merge authorization required separately. |
Zious11
added a commit
that referenced
this pull request
Jun 30, 2026
…tegration gate PASS; resume→F4 holdout STORY-146 (TLS buffer-saturation telemetry, PR #344 squash 8b52046) merged. Multi-pass convergence (doc-tense, saturating_add SEC-003 sibling-parity, EC-C1/EC-C3 coverage, 6→8 test-count header drift, accessor visibility parity, EC-C1 docstring attribution; 3 clean on bb29117). pr-reviewer APPROVE + security-reviewer CLEAR; 11/11 CI green. stories_delivered 93→94. Wave-66 integration gate PASS (2220/0 on 8b52046). Pre-F4 input-hash drift scan: MATCH=95 STALE=0 ERROR=3 (pre-existing structural issues: STORY-001 retired BC path, STORY-091+STORY-121 no inputs block). STORY-145=88e29c9 MATCH, STORY-146=6d9da65 MATCH. STATE.md: develop_head→8b52046, stories_delivered=94, story_index_version=v3.8, current_step→F4 holdout, EXACT RESUME POINT rewritten for holdout entry, D-310 added, TLS-SILENT-COMMENT-001 + TLS-SUMMARIZE-MAPTYPE-001 added to backlog, session checkpoint updated (Wave 66 COMPLETE; F4 holdout next). STORY-146.md: status draft→merged (v1.1 edits already present: saturating_add, 8-test documentation, BC-2.07.005 title fix, EC-C1 docstring attribution, revision history). STORY-INDEX.md: v3.7→v3.8; STORY-146 status draft→merged; wave-66 delivery row DELIVERED & CLOSED (#343, #344; develop head 8b52046). Count-propagation sweep: stories_delivered=93 remaining only in historical delta notations (92→93 for STORY-145) and D-309 decision row — intentional immutable audit trail. develop_head d3d2e19 remaining only in historical context (D-309, STORY-145 delta rows, v3.7 changelog line) — intentional.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
[STORY-146] TLS buffer-saturation telemetry — buffer_saturation_drops counter
Epic: TLS analyzer observability hardening — defense-in-depth for silent tail-drop (F-EV-001)
Mode: feature
Convergence: CONVERGED after 3 adversarial passes (fresh-context, bb29117)
Adds a
buffer_saturation_drops: u64aggregate telemetry counter toTlsAnalyzer. When aper-direction TCP-segment buffer (
client_buforserver_buf) reachesMAX_BUF = 65,536andtail-drops incoming bytes, the counter increments once per
on_datadrop event (both directionsshare the same aggregate). The counter is surfaced in
summarize()under the key"buffer_saturation_drops"— always present even at 0 (EC-008). This makes the previously-silenttail-drop non-silent and constitutes defense-in-depth for F-EV-001. Byte-drop semantics
(BC-2.07.005) are UNCHANGED — this is a telemetry/observability change only. Implemented with
saturating_add(1)(SEC-003 sibling consistency, avoids theoretical overflow-check panic underoverflow-checks = truein the release profile). Thefill_buf_for_testingseam is#[doc(hidden)]and test-only; it is not reachable from production input paths.Architecture Changes
graph TD TlsAnalyzer["TlsAnalyzer<br/>(src/analyzer/tls.rs)"] TlsFlowState["TlsFlowState<br/>(client_buf / server_buf)"] StreamHandler["StreamHandler::on_data"] summarize["StreamAnalyzer::summarize()"] newField["buffer_saturation_drops: u64<br/>(NEW — aggregate on TlsAnalyzer)"] newAccessor["buffer_saturation_drop_count()<br/>(NEW — public accessor)"] newSeam["fill_buf_for_testing()<br/>(NEW — #[doc(hidden)] test seam)"] newKey["'buffer_saturation_drops' key<br/>(NEW — in summarize() detail map)"] TlsAnalyzer -->|owns| TlsFlowState TlsAnalyzer -->|contains| newField StreamHandler -->|reads client_buf/server_buf| TlsFlowState StreamHandler -->|increments via saturating_add| newField summarize -->|inserts| newKey newAccessor -->|reads| newField newSeam -->|fills for test| TlsFlowState style newField fill:#90EE90 style newAccessor fill:#90EE90 style newSeam fill:#90EE90 style newKey fill:#90EE90Architecture Decision Record
ADR: Aggregate counter on TlsAnalyzer (not TlsFlowState)
Context: The per-direction buffer tail-drop path was previously silent — no caller could
observe that bytes were dropped, making F-EV-001 (silent drop) undetectable via telemetry.
Decision: Add
buffer_saturation_drops: u64as an aggregate counter onTlsAnalyzer(not on
TlsFlowState), initialized to 0, not reset onon_flow_close. Increment withsaturating_add(1)after the&mut stateborrow block closes (borrow-constraint mandated byRust's aliasing rules). Mirror the existing
truncated_records/handshake_reassembly_overflowsaggregate counter pattern.
Rationale: Aggregate counters on the analyzer struct (vs per-flow state) are the established
pattern in this codebase. Post-block placement is required by Rust borrow rules: the mutable
borrow on
self.flowsmust be released before accessingself.buffer_saturation_drops. Adid_drop: boolflag captures the condition inside the block and is read after.Alternatives Considered:
TlsFlowState— rejected: inconsistent withtruncated_records/handshake_reassembly_overflowspattern; adds complexity without benefit.checked_addwith panic — rejected: release profile setsoverflow-checks = true;saturating_addis SEC-003 compliant and matches sibling counter implementations.Consequences:
summarize()output (even at 0), closing the F-EV-001 silent-drop gap.fill_buf_for_testingseam is#[doc(hidden)]and gated bydebug_assert!onn <= MAX_BUF;not reachable from production input paths.
Story Dependencies
graph LR S144["STORY-144<br/>✅ MERGED<br/>TLS C2C reassembly"] --> S146["STORY-146<br/>🟡 this PR<br/>buffer saturation telemetry"] S145["STORY-145<br/>✅ MERGED<br/>TLS S2C reassembly"] --> S146 S146 --> future["(no downstream blocker)"] style S146 fill:#FFD700 style S144 fill:#90EE90 style S145 fill:#90EE90depends_on: STORY-144 (merged #341), STORY-145 (merged #343)
blocks: none
Spec Traceability
flowchart LR BC043["BC-2.07.043 v1.3<br/>buffer_saturation_drops<br/>telemetry"] --> AC001["AC-146-001<br/>fill_buf seam exists"] BC043 --> AC002["AC-146-002<br/>counter increments on drop"] BC043 --> AC004["AC-146-004<br/>accessor buffer_saturation_drop_count"] BC043 --> AC005["AC-146-005<br/>summarize() key always present"] BC043 --> AC006["AC-146-006<br/>both directions, same aggregate"] BC005["BC-2.07.005 v1.7 (amended)<br/>byte-drop semantics UNCHANGED"] --> AC003["AC-146-003<br/>byte semantics preserved"] AC001 --> T1["test_BC_2_07_043_buffer_saturation_observable"] AC002 --> T2["test_BC_2_07_043_buffer_saturation_full_drop"] AC004 --> T3["test_BC_2_07_043_counter_persists_across_flows"] AC005 --> T4["test_BC_2_07_043_summarize_value_equals_drop_count"] AC006 --> T5["test_BC_2_07_043_both_directions_increment_same_counter"] T1 --> SRC["src/analyzer/tls.rs"] T2 --> SRC T3 --> SRC T4 --> SRC T5 --> SRCVP-040: buffer_saturation_drops telemetry counter (BC-2.07.043 v1.3 + amended BC-2.07.005 v1.7)
Test Evidence
Coverage Summary
overflow-checks = true(release)Test Flow
graph LR Unit["8 New Unit Tests<br/>(mod story_146)"] Suite["148 Total<br/>(tls_analyzer_tests)"] Clippy["Clippy -D warnings"] Fmt["cargo fmt --check"] Unit -->|8/8 pass| Pass1["PASS"] Suite -->|148/148 pass| Pass2["PASS"] Clippy --> Pass3["PASS"] Fmt --> Pass4["PASS"] style Pass1 fill:#90EE90 style Pass2 fill:#90EE90 style Pass3 fill:#90EE90 style Pass4 fill:#90EE90Detailed Test Results
New Tests (mod story_146)
test_BC_2_07_043_buffer_saturation_observable()test_BC_2_07_043_buffer_saturation_full_drop()test_BC_2_07_043_no_drop_no_counter()test_BC_2_07_043_counter_persists_across_flows()test_BC_2_07_043_summarize_value_equals_drop_count()test_BC_2_07_043_both_directions_increment_same_counter()test_BC_2_07_043_partial_drop_boundary()test_BC_2_07_043_full_buffer_empty_data_no_count()Holdout Evaluation
N/A — evaluated at wave gate.
Adversarial Review
Convergence: CONVERGED — pass 3 produced 0 blocking findings; residuals were cosmetic (doc OBS-146-01 corrected in 2f561c3).
High-Severity Findings & Resolutions
Finding F-146-001: Stale RED/todo prose in comments/docs
src/analyzer/tls.rs(multiple comment blocks)todo!()and incomplete state.Finding F-146-02: Missing saturating_add for overflow safety
src/analyzer/tls.rs:on_data+= 1; release profile setsoverflow-checks = true, theoretical panic at u64::MAX.saturating_add(1)matching siblinghandshake_reassembly_overflowspattern (commit 6a57eaa).Finding F-146-ADV-01: Accessor visibility and test-count header mismatch
src/analyzer/tls.rs, README/docsbuffer_saturation_drop_countvisibility parity with siblings; test-count header said 6 (should be 8).Security Review
graph LR Critical["Critical: 0"] High["High: 0"] Medium["Medium: 0"] Low["Low: 0 unresolved"] style Critical fill:#90EE90 style High fill:#90EE90 style Medium fill:#90EE90 style Low fill:#87CEEBResult: CLEAR — 0 CRITICAL, 0 HIGH, 0 MEDIUM, 0 LOW findings.
Security review completed (STORY-146 is a SECURITY-CORRECTNESS cycle component — defense-in-depth telemetry for F-EV-001 silent tail-drop). All 5 assertions confirmed safe:
saturating_add(1)clamps at u64::MAX without panic; mirrors siblinghandshake_reassembly_overflows; correct foroverflow-checks=true. CONFIRMED SAFE.fill_buf_for_testingis#[doc(hidden)], has zero call sites outside test code; binary crate so external crate usage not a concern. CONFIRMED SAFE.did_dropis read-only comparison; does not alterto_copy,extend_from_slice, ortry_parse_recordsinvocation. CONFIRMED SAFE.Security Scan Details
Dependency Audit
cargo audit: run by CI (cargo-auditjob); expected CLEAN.cargo deny: run by CI; expected CLEAN.Formal Verification
saturating_addoverflow safetyoverflow-checks = truein release profilefill_buf_for_testingn <= MAX_BUFdebug_assert!Risk Assessment & Deployment
Blast Radius
src/analyzer/tls.rs(TlsAnalyzer struct + on_data + summarize),tests/tls_analyzer_tests.rssummarize()output gains one new key (buffer_saturation_drops); additive, not breaking.Performance Impact
Rollback Instructions
Immediate rollback (< 5 min):
Verification after rollback:
cargo test --all-targetspassessummarize()output no longer containsbuffer_saturation_dropskeyFeature Flags
Demo Evidence
wirerust analyze --tlssummary showsbuffer_saturation_drops: 0key presentdocs/demo-evidence/STORY-146/AC-146-005-summarize-key-present.webmstory_146tests pass (partial/full drop, both directions, persistence, value-equality)docs/demo-evidence/STORY-146/AC-146-001-002-004-006-test-suite.webmTraceability
test_BC_2_07_043_buffer_saturation_observabletest_BC_2_07_043_buffer_saturation_full_droptest_BC_2_07_043_no_drop_no_countertest_BC_2_07_043_counter_persists_across_flowstest_BC_2_07_043_summarize_value_equals_drop_counttest_BC_2_07_043_both_directions_increment_same_counterFull VSDD Contract Chain
AI Pipeline Metadata
Pipeline Details
Pre-Merge Checklist