go-session/parser_unix.go
Snider 92ecddaa69
Some checks are pending
Security Scan / security (push) Waiting to run
Test / test (push) Waiting to run
fix(session): r2 — platform-split no-follow + recursive convention scan + doc fixes on PR #5
Round 2 follow-up to 8ffd10c.

Code:
- parser_unix.go (new): Unix O_NOFOLLOW implementation
- parser_other.go (new): non-Unix fallback
- parser.go: removed syscall import; syscall failures wrapped via
  coreerr.E
- tests/cli/session/main.go: smoke driver uses core path/fs/string
  helpers (was using direct os + filepath + strings)

Tests:
- conventions_test.go: recursive Go file collection + nested-file test
  case (was non-recursive, missing nested files)

Doc:
- README.md: quick-start compile fix (fmt import + discard unused
  parse stats)
- kb/Home.md: ParseTranscript signature aligned to current API
  (captures and uses stats)

Verification: gofmt clean, golangci-lint v2 0 issues, GOWORK=off
go vet + go test -count=1 ./... pass with explicit cache paths.
AX-6 clean: no testify references; smoke driver uses core helpers.

Closes residual findings on https://github.com/dAppCore/go-session/pull/5

Co-authored-by: Codex <noreply@openai.com>
2026-04-27 18:48:40 +01:00

83 lines
2.3 KiB
Go

//go:build unix
// SPDX-Licence-Identifier: EUPL-1.2
package session
import (
"io" // Note: intrinsic — io.ReadCloser contract and EOF signalling for descriptor-backed transcript reads; no core equivalent
"syscall" // Note: intrinsic — O_NOFOLLOW descriptor opens and fstat checks are platform syscalls; no core equivalent
coreerr "dappco.re/go/core/log"
)
type noFollowFile struct {
fd int
}
// Read reads bytes from a descriptor opened without following symlinks.
func (f *noFollowFile) Read(p []byte) (int, error) {
n, err := syscall.Read(f.fd, p)
if err != nil {
return n, coreerr.E("noFollowFile.Read", "read transcript descriptor", err)
}
if n == 0 {
return 0, io.EOF
}
return n, nil
}
// Close closes a descriptor opened without following symlinks.
func (f *noFollowFile) Close() error {
if err := syscall.Close(f.fd); err != nil {
return coreerr.E("noFollowFile.Close", "close transcript descriptor", err)
}
return nil
}
// openTranscriptNoFollow opens a regular transcript file without following symlinks.
func openTranscriptNoFollow(filePath string) (io.ReadCloser, error) {
const op = "openTranscriptNoFollow"
fd, err := syscall.Open(filePath, syscall.O_RDONLY|syscall.O_NOFOLLOW, 0)
if err != nil {
return nil, coreerr.E(op, "open transcript without following symlinks", err)
}
var st syscall.Stat_t
if err := syscall.Fstat(fd, &st); err != nil {
if closeErr := closeNoFollowFD(fd); closeErr != nil {
return nil, closeErr
}
return nil, coreerr.E(op, "stat transcript descriptor", err)
}
if st.Mode&syscall.S_IFMT != syscall.S_IFREG {
if closeErr := closeNoFollowFD(fd); closeErr != nil {
return nil, closeErr
}
return nil, coreerr.E(op, "not a regular file", nil)
}
return &noFollowFile{fd: fd}, nil
}
// closeNoFollowFD closes a raw descriptor after a failed secure-open check.
func closeNoFollowFD(fd int) error {
if err := syscall.Close(fd); err != nil {
return coreerr.E("openTranscriptNoFollow", "close rejected transcript descriptor", err)
}
return nil
}
// isTranscriptMissing reports whether err wraps a missing transcript path error.
func isTranscriptMissing(err error) bool {
for err != nil {
if err == syscall.ENOENT {
return true
}
unwrapper, ok := err.(interface{ Unwrap() error })
if !ok {
return false
}
err = unwrapper.Unwrap()
}
return false
}