-
Notifications
You must be signed in to change notification settings - Fork 11
protocol safety bits: forceTrim comment, gen5 dangerous opcodes, bad frame rev #26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -826,6 +826,13 @@ class Decoded { | |
| /// scale, GET_HELLO opcode, historical-record version family); defaults to | ||
| /// gen4 so every existing caller is unchanged. | ||
| Decoded decodeFrame(Frame frame, {BandProfile profile = BandProfile.gen4}) { | ||
| // A frame that passed CRC but advertises a frame revision this decoder does | ||
| // not understand (see [Frame.frameRevOk]) must NOT be decoded with the rev-1 | ||
| // `inner[0]/[1]/[2]` field offsets — surface it instead of silently handing | ||
| // back a body byte as the packet type / opcode. | ||
| if (!frame.frameRevOk) { | ||
| return Decoded('unsupported_frame_rev', {'packet_type': frame.packetType}); | ||
| } | ||
|
Comment on lines
+829
to
+835
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
ast-grep outline lib/src/framing.dart --items all
rg -n -C 5 'class Frame\b|packetType|frameRevOk|inner\[' lib/src/framing.dart
rg -n -C 5 'unsupported_frame_rev|frame\.packetType' lib/src/control.dartRepository: OpenStrap/protocol Length of output: 3876 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the result shape and existing tests/usages to determine whether
# unsupported_frame_rev requires packet_type.
rg -n -C 6 'class Decoded\b|Decoded\(|unsupported_frame_rev|packet_type' lib test* 2>/dev/null || true
# Behavior probe from the getter definition: packetType reads inner[0].
python3 - <<'PY'
inner = bytes([0xA5, 0x10, 0x20, 0x99])
packet_type = inner[0] if len(inner) else -1
print({"unsupported_revision_inner": list(inner), "reported_packet_type": packet_type})
PYRepository: OpenStrap/protocol Length of output: 8004 Do not read revision-1 fields for unsupported revisions.
🤖 Prompt for AI Agents |
||
| final inner = frame.inner; | ||
| final pt = frame.packetType; | ||
| try { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,12 +23,30 @@ class Frame { | |
| final bool crc8Ok; | ||
| final bool crc32Ok; | ||
|
|
||
| Frame(this.inner, this.crc8Ok, this.crc32Ok); | ||
| /// Whether the frame header advertises the frame revision this decoder was | ||
| /// written for (rev-1). The `inner[0]/[1]/[2]` = packetType/seq/opcode field | ||
| /// offsets below assume rev-1 layout; a rev-2 frame can still pass both CRCs | ||
| /// yet shift those fields, so [opcode] would silently return a body byte. | ||
| /// | ||
| /// gen5 carries an explicit revision byte at header[1] (0x01 on every real | ||
| /// strap frame — see [BandProfile.buildHeader]); a rev-2 frame stamps 0x02 | ||
| /// there. gen4's 4-byte header has no revision byte, so it is always treated | ||
| /// as rev-1. Defaults true so directly-constructed frames and the entire | ||
| /// rev-1 path are unchanged; [parseFrame] sets it false for a gen5 frame | ||
| /// whose revision byte is not rev-1 — surfacing it instead of mis-decoding. | ||
| final bool frameRevOk; | ||
|
|
||
| Frame(this.inner, this.crc8Ok, this.crc32Ok, {this.frameRevOk = true}); | ||
|
|
||
| /// Band-neutral alias for the header-integrity result. | ||
| bool get headerCrcOk => crc8Ok; | ||
|
|
||
| bool get valid => crc8Ok && crc32Ok; | ||
|
|
||
| /// Safe to read [packetType]/[seq]/[opcode] with the rev-1 field offsets: | ||
| /// CRCs pass AND the frame revision is one this decoder understands. Check | ||
| /// this (not just [valid]) before trusting [opcode] on an inbound frame. | ||
| bool get decodable => valid && frameRevOk; | ||
|
Comment on lines
+46
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect Frame parsing and validation consumers with surrounding context.
rg -n --type dart -C5 '\bparseFrame\s*\(|\.(valid|decodable)\b' lib testRepository: OpenStrap/protocol Length of output: 38092 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Enumerate production parsing, reassembly, and decoding paths, then inspect
# the relevant implementations and call sites.
printf '%s\n' '--- production parse/decode/reassembly references ---'
rg -n --type dart -C4 '\b(parseFrame|decodeFrame|FrameReassembler)\b|\.(valid|decodable|packetType|seq|opcode)\b' lib
printf '%s\n' '--- framing.dart structure and implementation ---'
ast-grep outline lib/src/framing.dart
sed -n '1,210p' lib/src/framing.dart
printf '%s\n' '--- decoder-related files ---'
rg -l --type dart '\bdecodeFrame\b|\bparseFrame\b|\bFrameReassembler\b' lib | sortRepository: OpenStrap/protocol Length of output: 16589 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- production parse/decode/reassembly references ---'
rg -n --type dart -C4 '\b(parseFrame|decodeFrame|FrameReassembler)\b|\.(valid|decodable|packetType|seq|opcode)\b' lib
printf '%s\n' '--- framing.dart structure and implementation ---'
ast-grep outline lib/src/framing.dart
sed -n '1,210p' lib/src/framing.dart
printf '%s\n' '--- decoder-related files ---'
rg -l --type dart '\bdecodeFrame\b|\bparseFrame\b|\bFrameReassembler\b' lib | sortRepository: OpenStrap/protocol Length of output: 16589 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- all repository call sites outside generated/build directories ---'
rg -n -g '!**/.git/**' -g '!**/build/**' -g '!**/dist/**' \
'\b(parseFrame|decodeFrame|FrameReassembler)\s*(\(|<)|\.(valid|decodable)\b' .
printf '%s\n' '--- decodeFrame implementation and nearby parsers ---'
sed -n '430,520p' lib/src/control.dart
sed -n '800,900p' lib/src/control.dart
printf '%s\n' '--- BandProfile definitions and profile propagation ---'
rg -n -C8 '\b(class BandProfile|BandProfile\.gen[45]|profile\s*[:=])\b' lib test README.md
printf '%s\n' '--- public usage documentation ---'
rg -n -C5 '\b(parseFrame|decodeFrame|FrameReassembler|BandProfile)\b' README.md docs example tool 2>/dev/null || trueRepository: OpenStrap/protocol Length of output: 50374 🏁 Script executed: #!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
root = Path(".")
dart_files = [
p for p in root.rglob("*.dart")
if ".git" not in p.parts and "build" not in p.parts and "dist" not in p.parts
]
print("--- production parseFrame call classification ---")
for p in sorted(dart_files):
if "test" in p.parts:
continue
for n, line in enumerate(p.read_text().splitlines(), 1):
if re.search(r"\bparseFrame\s*\(", line):
print(f"{p}:{n}: {line.strip()}")
print("--- production FrameReassembler construction classification ---")
for p in sorted(dart_files):
if "test" in p.parts:
continue
for n, line in enumerate(p.read_text().splitlines(), 1):
if "FrameReassembler(" in line:
print(f"{p}:{n}: {line.strip()}")
print("--- decodeFrame dispatch ordering ---")
control = Path("lib/src/control.dart").read_text().splitlines()
start = next(i for i, s in enumerate(control) if s.startswith("Decoded decodeFrame("))
end = next(i for i in range(start + 1, len(control)) if control[i].startswith("Decoded _decodeDataRecord"))
body = control[start:end]
for i, s in enumerate(body, start + 1):
if re.search(r"\b(frame\.decodable|frame\.valid|frame\.packetType|frame\.seq|frame\.opcode)\b", s):
print(f"lib/src/control.dart:{i}: {s.strip()}")
packet = next(i for i, s in enumerate(body) if "frame.packetType" in s)
guard = next((i for i, s in enumerate(body) if "frame.decodable" in s or "frame.valid" in s), None)
print(f"packet_type_read_index={packet}")
print(f"decodability_guard_index={guard}")
print("decodeFrame_guards_decodability=",
guard is not None and guard < packet)
PY
printf '%s\n' '--- focused test consumers that decode or dereference parsed frames ---'
rg -n -C2 --type dart \
'parseFrame|decodeFrame|FrameReassembler|parsed\.inner|frame\.inner|\.packetType|\.seq|\.opcode' test \
| rg -v '^\s*(//|test/[^:]+:[0-9]+-\s*(test|group|expect|final|late|return|}))' \
| head -n 220Repository: OpenStrap/protocol Length of output: 15265 Guard
🤖 Prompt for AI Agents |
||
| int get packetType => inner.isNotEmpty ? inner[0] : -1; | ||
| int get seq => inner.length > 1 ? inner[1] : -1; | ||
| int get opcode => inner.length > 2 ? inner[2] : -1; | ||
|
|
@@ -81,7 +99,13 @@ Frame? parseFrame(Uint8List raw, {BandProfile profile = BandProfile.gen4}) { | |
| final storedBd = | ||
| raw.buffer.asByteData(raw.offsetInBytes + innerStart + declared - 4, 4); | ||
| final stored = storedBd.getUint32(0, Endian.little); | ||
| return Frame(Uint8List.fromList(inner), headerCrcOk, stored == crc32(inner)); | ||
| // gen5 header[1] is the frame-revision byte (rev-1 = 0x01). A rev-2 frame can | ||
| // pass both CRCs but shifts the inner field offsets, so flag it rather than | ||
| // let the rev-1 getters return a body byte as the opcode. gen4 has no such | ||
| // byte and is always rev-1. | ||
| final frameRevOk = !profile.isGen5 || raw[1] == revision1; | ||
| return Frame(Uint8List.fromList(inner), headerCrcOk, stored == crc32(inner), | ||
| frameRevOk: frameRevOk); | ||
| } | ||
|
|
||
| /// Length-based reassembler. feed() returns every complete Frame it can carve | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.