Skip to content
Merged
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
36 changes: 18 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ jobs:
go: ["1.25", "1.26"]

steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5

- uses: actions/setup-go@v6
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: ${{ matrix.go }}

Expand All @@ -36,7 +36,7 @@ jobs:

- name: Upload coverage
if: matrix.os == 'ubuntu-latest' && matrix.go == '1.25'
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: coverage
path: coverage.out
Expand All @@ -48,9 +48,9 @@ jobs:
name: Coverage floor (library packages >= 85%)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5

- uses: actions/setup-go@v6
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: "1.25"

Expand Down Expand Up @@ -79,9 +79,9 @@ jobs:
name: Benchmark smoke (ubuntu-latest)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5

- uses: actions/setup-go@v6
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: "1.25"

Expand All @@ -103,9 +103,9 @@ jobs:
name: Fuzz (short, ubuntu-latest)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5

- uses: actions/setup-go@v6
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: "1.25"

Expand All @@ -129,9 +129,9 @@ jobs:
name: RELAY conform (--strict)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5

- uses: actions/setup-go@v6
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: "1.25"

Expand All @@ -151,9 +151,9 @@ jobs:
name: RELAY interop (LIN)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5

- uses: actions/setup-go@v6
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: "1.25"

Expand All @@ -177,9 +177,9 @@ jobs:
name: go-FuSa full lifecycle (v0.48.0)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5

- uses: actions/setup-go@v6
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: "1.25"

Expand Down Expand Up @@ -260,7 +260,7 @@ jobs:

- name: Upload safety evidence
if: always()
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: gofusa-safety-evidence
path: gofusa-audit-pack.zip
Expand All @@ -275,9 +275,9 @@ jobs:
name: Standards compliance (ISO / IEC / DO / UNECE / SLSA)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5

- uses: actions/setup-go@v6
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: "1.25"

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/dco.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
name: Developer Certificate of Origin
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
fetch-depth: 0

Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,16 @@ jobs:
target: [go-lin, quickstart, lintool]

steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5

- name: Set up QEMU (multi-arch)
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3

- name: Log in to GHCR
uses: docker/login-action@v3
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
Expand All @@ -54,7 +54,7 @@ jobs:

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5
with:
images: ${{ steps.image.outputs.name }}
tags: |
Expand All @@ -64,7 +64,7 @@ jobs:
type=sha,prefix=sha-,format=short

- name: Build and push
uses: docker/build-push-action@v6
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: .
file: docker/Dockerfile
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v5
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
fetch-depth: 0
ref: main

- uses: actions/setup-go@v6
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: "1.25"

Expand Down
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,38 @@ canonical list. Dates are release dates (UTC-7, matching tag creation).

## [Unreleased]

- fix: `ldf.parseFrameHeader` now rejects (rather than silently corrupting)
a frame whose ID is outside 0x00–0x3F or whose declared length is
outside 0–8 bytes — previously a negative length (e.g. from
`f: 0x10, MASTER, -4;`) reached `DB.Encode`'s `make([]byte, f.Length)`
and panicked (`runtime error: makeslice: len out of range`, CWE-789 DoS
on untrusted LDF input), and an out-of-range ID (e.g. `300`) was
silently truncated via a bare `uint8()` cast, corrupting whatever frame
already lived at the truncated ID. A frame rejected this way is now
properly skipped rather than swallowing every subsequent frame in the
same `Frames` section (the previous single-`continue` mistook the
rejected frame's own closing brace for the section's closing brace).
Signal-ref bit offsets with a negative value (also previously discarded
the parse error) are now rejected the same way rather than relying on
incidental Go shift/comparison semantics to avoid a panic (#76)
- fix(safety): `safety.Receiver.Unwrap` now compares the wire-transmitted
`DataID`/`SourceID` against the receiver's configured `Config` and
returns a new `ErrIDMismatch` on mismatch — previously the CRC check
alone was (incorrectly, per the code's own now-corrected comment)
treated as sufficient masquerade protection, so a frame protected under
a different `DataID`/`SourceID` than the receiver's was accepted
without error (#76)
- fix(virtual): `Bus.Publish`/`Bus.PublishClassic` now reject a non-nil,
zero-length payload the same way they already reject an over-length
one, so the virtual bus can never broadcast a 0-data-byte frame that
`lin.ValidateFrame` itself would consider malformed (LIN Specification
Package 2.2A: the data field carries 1–8 bytes); `PublishClassic` also
gained the `LINMaxDataLen` over-length guard `Publish` already had (#76)
- chore(ci): pinned all third-party and first-party GitHub Actions in
`.github/workflows/` to immutable commit SHAs (with a `# vX` comment for
readability) instead of mutable version tags, matching the repo's own
SLSA/supply-chain evidence posture (#76)

## [1.5.0] — 2026-07-30

- chore: bump `github.com/SoundMatt/RELAY` v1.11.0 → `github.com/SoundMatt/RELAY/v2`
Expand Down
26 changes: 24 additions & 2 deletions ldf/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,15 @@ func (p *ldfParser) parseFrames(db *DB) error {
p.next()
fr, err := parseFrameHeader(line)
if err != nil {
// Skip past this frame's body (up to and including its
// closing brace) so a single invalid frame header does not
// swallow subsequently well-formed frames or get mistaken
// for the closing brace of the whole Frames section.
for p.pos < len(p.lines) {
if p.next() == "}" {
break
}
}
continue
}
// parse signal refs
Expand All @@ -456,7 +465,14 @@ func (p *ldfParser) parseFrames(db *DB) error {
parts := strings.SplitN(inner, ",", 2)
if len(parts) == 2 {
sigName := strings.TrimSpace(parts[0])
offset, _ := parseInt(strings.TrimSpace(parts[1]))
offset, err := parseInt(strings.TrimSpace(parts[1]))
if err != nil || offset < 0 {
// Malformed or negative bit offset: skip this signal
// ref rather than let extractBits/packBits receive a
// negative offset (would rely on incidental Go shift/
// comparison semantics to avoid a panic).
continue
}
fr.Signals = append(fr.Signals, SignalRef{Name: sigName, BitOffset: int(offset)})
}
}
Expand Down Expand Up @@ -486,8 +502,14 @@ func parseFrameHeader(line string) (*Frame, error) {
if err != nil {
return nil, fmt.Errorf("ldf: invalid frame ID in %q: %w", line, err)
}
if id < 0 || id > int64(lin.LINMaxID) {
return nil, fmt.Errorf("ldf: frame ID %d out of range 0-%d in %q", id, lin.LINMaxID, line)
}
publisher := strings.TrimSpace(parts[1])
length, _ := parseInt(strings.TrimSpace(parts[2]))
length, err := parseInt(strings.TrimSpace(parts[2]))
if err != nil || length < 0 || length > int64(lin.LINMaxDataLen) {
return nil, fmt.Errorf("ldf: frame length out of range 0-%d in %q", lin.LINMaxDataLen, line)
}
return &Frame{
Name: name,
ID: uint8(id),
Expand Down
Loading
Loading