package engine import ( "errors" "io" "io/fs" "os" ) type sourceReadFailureKind uint8 const ( sourceReadIO sourceReadFailureKind = iota sourceReadNotRegular sourceReadLimit ) // sourceReadFailure never escapes the private source boundary. missingTarget is // set only for preflight/open failures; a later missing-file error is not optional. type sourceReadFailure struct { kind sourceReadFailureKind cause error missingTarget bool } // readRegularFile borrows stat/open operations so root-bound callers retain their // containment policy. Preflight rejects FIFOs before open can block. The opened // handle is checked again and always closed; read-only close errors are ignored. func readRegularFile(name string, stat func(string) (fs.FileInfo, error), open func(string) (*os.File, error), limit int64) ([]byte, *sourceReadFailure) { info, err := stat(name) if err != nil { return nil, &sourceReadFailure{cause: err, missingTarget: errors.Is(err, fs.ErrNotExist)} } if !info.Mode().IsRegular() { return nil, &sourceReadFailure{kind: sourceReadNotRegular} } file, err := open(name) if err != nil { return nil, &sourceReadFailure{cause: err, missingTarget: errors.Is(err, fs.ErrNotExist)} } defer func() { _ = file.Close() }() info, err = file.Stat() if err != nil { return nil, &sourceReadFailure{cause: err} } if !info.Mode().IsRegular() { return nil, &sourceReadFailure{kind: sourceReadNotRegular} } if info.Size() > limit { return nil, &sourceReadFailure{kind: sourceReadLimit} } return readBoundedSource(file, limit) } func readBoundedSource(input io.Reader, limit int64) ([]byte, *sourceReadFailure) { data, err := io.ReadAll(io.LimitReader(input, limit+1)) if err != nil { return nil, &sourceReadFailure{cause: err} } if int64(len(data)) > limit { return nil, &sourceReadFailure{kind: sourceReadLimit} } return data, nil } func sourceReadDiagnostic(failure *sourceReadFailure, sourceID string, limitCode Code, limitMessage string) Diagnostic { switch failure.kind { case sourceReadNotRegular: return sourceDiagnostic(CodeSourceNotRegular, sourceID, "source is not a regular file") case sourceReadLimit: return sourceDiagnostic(limitCode, sourceID, limitMessage) default: return classifySourceError(failure.cause, sourceID) } }