mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-03 20:19:07 +02:00
Request/response parsers and SSE/event-stream metering, the embedded pricing table, and the builtin middleware set: request parser, router, policy limit-check/record, cost meter, guardrail, identity inject, response parser. Includes the path-routed providers — Google Vertex AI (keyfile:: service-account OAuth minting) and AWS Bedrock (bearer auth, invoke/converse/streaming, optional /bedrock prefix) — plus the Models allowlist and unmeterable-publisher deny.
69 lines
2.0 KiB
Go
69 lines
2.0 KiB
Go
//go:build unix
|
|
|
|
package pricing
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"syscall"
|
|
"time"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// maxPricingBytes caps the size of the pricing YAML on read so a hostile or
|
|
// runaway file cannot exhaust process memory during reload. 1 MiB is several
|
|
// orders of magnitude larger than any reasonable pricing table.
|
|
const maxPricingBytes int64 = 1 << 20
|
|
|
|
// loadPricing opens the file with O_NOFOLLOW, fstats the open descriptor,
|
|
// and parses from that same descriptor. Never re-opens by path so a
|
|
// mid-read rename or symlink swap cannot substitute content. Bytes are
|
|
// capped at maxPricingBytes so the loader cannot be coerced into reading an
|
|
// unbounded file.
|
|
func loadPricing(path string) (*Table, time.Time, error) {
|
|
f, err := os.OpenFile(path, os.O_RDONLY|syscall.O_NOFOLLOW, 0)
|
|
if err != nil {
|
|
return nil, time.Time{}, fmt.Errorf("open %s: %w", path, err)
|
|
}
|
|
defer func() {
|
|
if cerr := f.Close(); cerr != nil {
|
|
log.Debugf("close pricing file %s: %v", path, cerr)
|
|
}
|
|
}()
|
|
|
|
info, err := f.Stat()
|
|
if err != nil {
|
|
return nil, time.Time{}, fmt.Errorf("fstat %s: %w", path, err)
|
|
}
|
|
if !info.Mode().IsRegular() {
|
|
return nil, time.Time{}, fmt.Errorf("pricing file %s is not a regular file", path)
|
|
}
|
|
|
|
data, err := io.ReadAll(io.LimitReader(f, maxPricingBytes+1))
|
|
if err != nil {
|
|
return nil, time.Time{}, fmt.Errorf("read %s: %w", path, err)
|
|
}
|
|
if int64(len(data)) > maxPricingBytes {
|
|
return nil, time.Time{}, fmt.Errorf("pricing file %s exceeds %d bytes", path, maxPricingBytes)
|
|
}
|
|
|
|
table, err := parsePricingBytes(data)
|
|
if err != nil {
|
|
return nil, time.Time{}, err
|
|
}
|
|
return table, info.ModTime(), nil
|
|
}
|
|
|
|
// statMtime returns the mtime of the file at path. It uses lstat semantics
|
|
// via os.Lstat so a symlink swap is detected even though O_NOFOLLOW will
|
|
// later reject the open.
|
|
func statMtime(path string) (time.Time, error) {
|
|
info, err := os.Lstat(path)
|
|
if err != nil {
|
|
return time.Time{}, fmt.Errorf("lstat %s: %w", path, err)
|
|
}
|
|
return info.ModTime(), nil
|
|
}
|