Skip to content

chore: back-merge release v0.11.4 into develop#368

Merged
Zious11 merged 13 commits into
developfrom
chore/backmerge-v0.11.4
Jul 6, 2026
Merged

chore: back-merge release v0.11.4 into develop#368
Zious11 merged 13 commits into
developfrom
chore/backmerge-v0.11.4

Conversation

@Zious11

@Zious11 Zious11 commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Summary

Back-merge release v0.11.4 from main into develop to keep branches in sync (CLAUDE.md gitflow requirement).

Merge method

Squash merge (develop has required_linear_history).

Zious11 added 13 commits June 29, 2026 07:45
## Summary

Release v0.11.0 — ships seven bug fixes across the ENIP, DNP3, and
Modbus analyzers (EC-X1/EC-X2 carry-buffer direction split and
`saturating_sub` clock-backwards fix applied to all three protocols,
plus the DNP3 desync-latch `frame_count == 0` guard). All fixes landed
on `develop` via PRs #334 (ENIP), #335 (DNP3), and #336 (Modbus + DNP3
desync-latch). This PR is the gitflow release merge from
`release/0.11.0` → `main`.

---

## What Changed

### Fixed — EC-X1 (per-direction carry buffer split)

All three ICS analyzers previously used a **single shared carry buffer**
for both TCP directions (client→server and server→client). A response
packet's trailing bytes could be spliced into the next request's
reassembly window (cross-direction carry-buffer contamination). Each
carry buffer is now split into two independent fields keyed by direction
(`carry_c2s` / `carry_s2c`).

| Protocol | Story | PR | Behavioral Contract |
|----------|-------|----|---------------------|
| ENIP     | STORY-139 | #334 | BC-2.17.EC-X1 |
| DNP3     | STORY-140 | #335 | BC-2.15.EC-X1 |
| Modbus   | STORY-141 | #336 | BC-2.14.EC-X1 |

### Fixed — EC-X2 (`saturating_sub` for clock-backwards window reset)

A non-monotonic timestamp (packet re-ordering or NTP step) caused the
time-delta computation in the window-reset path to underflow via
wrapping subtraction on an unsigned value. The subtraction now uses
`saturating_sub`, keeping window-reset logic correct when clocks move
backwards.

| Protocol | Story | PR | Behavioral Contract |
|----------|-------|----|---------------------|
| ENIP     | STORY-139 | #334 | BC-2.17.EC-X2 |
| DNP3     | STORY-140 | #335 | BC-2.15.EC-X2 |
| Modbus   | STORY-141 | #336 | BC-2.14.EC-X2 |

### Fixed — DNP3 desync-latch complete-predicate

The DNP3 desync-latch complete-predicate fired unconditionally, which
could produce a spurious desync event on the very first frame of a
session before any real desync had occurred. The predicate is now gated
on `frame_count == 0` so it only triggers after at least one valid frame
has been observed.

| Protocol | Story | PR | Behavioral Contract |
|----------|-------|----|---------------------|
| DNP3     | STORY-142 | #336 | BC-2.15.DESYNC |

---

## Architecture Changes

```mermaid
graph TD
    A[src/analyzer/enip.rs] -->|carry_c2s / carry_s2c split| B[ENIP EC-X1 fix]
    A -->|saturating_sub delta| C[ENIP EC-X2 fix]
    D[src/analyzer/dnp3.rs] -->|carry_c2s / carry_s2c split| E[DNP3 EC-X1 fix]
    D -->|saturating_sub delta| F[DNP3 EC-X2 fix]
    D -->|frame_count == 0 guard| G[DNP3 desync-latch fix]
    H[src/analyzer/modbus.rs] -->|carry_c2s / carry_s2c split| I[Modbus EC-X1 fix]
    H -->|saturating_sub delta| J[Modbus EC-X2 fix]
```

---

## Story Dependencies

```mermaid
graph LR
    S139[STORY-139 ENIP EC-X1/EC-X2] --> PR334[PR #334 merged]
    S140[STORY-140 DNP3 EC-X1/EC-X2] --> PR335[PR #335 merged]
    S141[STORY-141 Modbus EC-X1/EC-X2] --> PR336[PR #336 merged]
    S142[STORY-142 DNP3 desync-latch] --> PR336
    PR334 --> DEV[develop @ a13b5c5]
    PR335 --> DEV
    PR336 --> DEV
    DEV --> REL[release/0.11.0 @ ba31a0c]
    REL --> MAIN[main]
```

---

## Spec Traceability

```mermaid
flowchart LR
    BC1[BC-2.14.EC-X1] --> AC1[AC-141-001] --> T1[modbus_detection_tests.rs] --> Code1[modbus.rs carry split]
    BC2[BC-2.14.EC-X2] --> AC2[AC-141-002] --> T2[modbus_detection_tests.rs] --> Code2[modbus.rs saturating_sub]
    BC3[BC-2.15.EC-X1] --> AC3[AC-140-001] --> T3[dnp3_f6_story140_mutation_tests.rs] --> Code3[dnp3.rs carry split]
    BC4[BC-2.15.EC-X2] --> AC4[AC-140-004] --> T4[dnp3_detection_tests.rs] --> Code4[dnp3.rs saturating_sub]
    BC5[BC-2.15.DESYNC] --> AC5[AC-142-001] --> T5[dnp3_detection_tests.rs] --> Code5[dnp3.rs frame_count guard]
    BC6[BC-2.17.EC-X1] --> AC6[AC-139-001] --> T6[enip_analyzer_tests.rs] --> Code6[enip.rs carry split]
    BC7[BC-2.17.EC-X2] --> AC7[AC-139-002] --> T7[enip_analyzer_tests.rs] --> Code7[enip.rs saturating_sub]
```

---

## Test Evidence

- All tests pass on `develop` @ a13b5c5 (CI run 28335634278, green)
- `release/0.11.0` adds only CHANGELOG + Cargo.toml version bump on top
— no logic changes
- Test files modified/added: `enip_analyzer_tests.rs` (+9229 lines),
`dnp3_f6_story140_mutation_tests.rs` (+1667),
`modbus_detection_tests.rs` (+1539), `enip_e2e_real_pcaps_tests.rs`
(+803), `dnp3_f6_story140_group_a_survivors.rs` (+866), plus updates to
existing dnp3/modbus/dispatcher test suites

---

## Demo Evidence

Per-AC VHS recordings are in `docs/demo-evidence/`:
- `STORY-139/evidence-report.md` — ENIP EC-X1/EC-X2 (AC-139-001 through
AC-139-004)
- `STORY-140/evidence-report.md` — DNP3 EC-X1/EC-X2 + desync-latch
(AC-140-001, 004, 005, 008)

---

## Security Review

No new attack surface introduced. All changes are defensive: replacing
wrapping arithmetic with saturating arithmetic, and splitting shared
mutable buffers into direction-keyed fields. No new public API surface,
no new deserialization paths, no new network-facing code.

---

## Risk Assessment

- **Blast radius:** Contained to the three ICS analyzer structs. No
changes to the CLI, dispatcher, pcap reader, or reporting pipeline.
- **Performance impact:** Negligible. `saturating_sub` compiles to the
same instruction count as wrapping sub on x86-64. The carry buffer split
doubles the struct field count for the affected analyzers (2 fields → 4
fields per analyzer), which is a trivially small allocation increase.
- **Behavioral change:** Bug-fixes only. Sessions that previously had no
cross-direction contamination produce identical output. Sessions that
had contamination now produce correct output (previously incorrect
behavior is fixed).

---

## AI Pipeline Metadata

- Pipeline mode: Feature (gitflow release)
- Stories: STORY-139, STORY-140, STORY-141, STORY-142
- Prior CI run on develop: 28335634278 (green)

---

## Pre-Merge Checklist

- [x] CHANGELOG entry written (v0.11.0 section in CHANGELOG.md)
- [x] Cargo.toml version bumped 0.10.0 → 0.11.0
- [x] Cargo.lock updated
- [x] All upstream PRs merged (#334, #335, #336)
- [x] CI green on develop @ a13b5c5
- [ ] CI green on release/0.11.0 PR (awaiting CI run)
- [x] PR title follows semantic PR convention: `chore: release v0.11.0`
- [x] Base branch: main
- [x] Merge strategy: squash
- Bump crate version 0.11.0 → 0.11.1
- Add CHANGELOG.md [0.11.1] section (TLS reassembly fix, buffer-saturation
  telemetry, VP-039 formal hardening, anyhow RUSTSEC-2026-0190 bump)
- Update compare links ([Unreleased] and new [0.11.1] entry)
The v0.11.0 release was squash-merged to main (PR #337) without a
subsequent back-merge into develop. This merge commit establishes
shared history between main and release/0.11.1 so the release PR
(#347) can be cleanly merged.

Conflict resolution: release/0.11.1 versions kept for all 6 conflicts
(CHANGELOG.md, Cargo.toml, Cargo.lock, ci.yml, fuzz/Cargo.toml,
fuzz/Cargo.lock) — release branch has the correct v0.11.1 versions
and the complete v0.11.0 CHANGELOG section.
Bump Cargo.toml + Cargo.lock: 0.11.1 → 0.11.2.
Add CHANGELOG.md section for v0.11.2 (2026-07-05) covering the E-21
feature-protocol-coverage delta: protocols subcommand (STORY-152),
--coverage-gaps flag / CoverageGapsSummary (STORY-154), KNOWN_PROTOCOLS
catalog + partition functions (STORY-151), dispatcher unclassified-protocol
gap counters (STORY-153), and F6 formal hardening VP-041/042/043 (#357).
The v0.11.1 release was merged to main (PR #347) via a merge commit that
included a history-establishment step (20f3756). That commit is not an
ancestor of release/0.11.2, causing a CONFLICTING merge state on PR #358.
This merge commit establishes shared history between main and release/0.11.2
so the release PR can be cleanly merged.

Conflict resolution: release/0.11.2 versions kept for all 3 conflicts
(CHANGELOG.md, Cargo.toml, Cargo.lock) — release branch has the correct
v0.11.2 versions and the complete v0.11.2 CHANGELOG section.
Bump version 0.11.2 → 0.11.3, update Cargo.lock, and add CHANGELOG
section for the DNP3/ENIP per-flow memory fix (issue #342, PR #362).
# Conflicts:
#	CHANGELOG.md
#	Cargo.lock
#	Cargo.toml
@Zious11 Zious11 merged commit f7460b4 into develop Jul 6, 2026
27 checks passed
@Zious11 Zious11 deleted the chore/backmerge-v0.11.4 branch July 6, 2026 18:04
Zious11 added a commit that referenced this pull request Jul 6, 2026
…D-386)

v0.11.4 RELEASED 2026-07-06:
- PR #366 (squash to develop): 3 silent-limit follow-up items RESOLVED:
    HTTP-AC008-NEG-TEST-001 (negative regression test, no dropped_map_entries on hit)
    EVICTION-NO-FINDING-NEG-TEST-001 (Modbus + ARP eviction emit no Finding)
    ARP-BINDINGS-EVICT-PRECHECK-COSMETIC-001 (insert_binding_lru returns bool; dedup)
- PR #367 release/0.11.4 → main (merge f0f2136); tag v0.11.4 (obj e6ee614)
- PR #368 back-merge main → develop (squash f7460b4); Cargo.toml 0.11.4
- develop has NO unreleased commits; pipeline IDLE

Informational note added (REBIND-COUNT-SATURATING-001): rebind_count in
src/analyzer/arp.rs uses plain += — pre-existing LOW/informational, same class
as cleared SEC-004+SEC-007; DF-VALIDATION-001-gated before any issue.

STATE.md updates: frontmatter (released_version, HEADs, cargo_version, timestamp,
current_step), EXACT RESUME POINT, Project Metadata table, Phase Progress new row,
Current Phase Steps rotation, Decisions Log D-386, Backlog (3 RESOLVED + 1 new LOW),
Session Resume Checkpoint (develop f7460b4 / main f0f2136, NO unreleased commits).
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.

1 participant