fix(gateway): enforce audited production traversal

This commit is contained in:
sechmachine
2026-07-30 04:48:02 +07:00
parent d3852d15f3
commit 70667d5aee
17 changed files with 1368 additions and 317 deletions
+52 -18
View File
@@ -5,6 +5,7 @@ import (
"encoding/xml"
"errors"
"fmt"
"strconv"
"strings"
"sync"
"time"
@@ -66,8 +67,12 @@ func (i ProviderIdentity) Validate(now time.Time, expected ProviderIdentity) err
}
type ManagementInfo struct {
Identity ProviderIdentity
Name string
Identity ProviderIdentity
Name string
ServerCodecModeSupport uint32
MaxLumaPixelsHEVC uint64
HasServerCodecModeSupport bool
HasMaxLumaPixelsHEVC bool
}
func ParseManagementXML(data []byte) (ManagementInfo, error) {
@@ -82,6 +87,8 @@ func ParseManagementXML(data []byte) (ManagementInfo, error) {
NotBefore string `xml:"not_before"`
NotAfter string `xml:"not_after"`
Name string `xml:"name"`
CodecModes string `xml:"ServerCodecModeSupport"`
MaxHEVCLuma string `xml:"MaxLumaPixelsHEVC"`
}
decoder := xml.NewDecoder(strings.NewReader(string(data)))
decoder.Strict = true
@@ -108,7 +115,24 @@ func ParseManagementXML(data []byte) (ManagementInfo, error) {
if identity.UniqueID == "" || len(identity.UniqueID) > 128 || len(identity.Fingerprint) > 256 {
return ManagementInfo{}, ErrProviderMalformed
}
return ManagementInfo{Identity: identity, Name: document.Name}, nil
info := ManagementInfo{Identity: identity, Name: document.Name}
if document.CodecModes != "" {
value, parseErr := strconv.ParseUint(document.CodecModes, 10, 32)
if parseErr != nil {
return ManagementInfo{}, ErrProviderMalformed
}
info.ServerCodecModeSupport = uint32(value)
info.HasServerCodecModeSupport = true
}
if document.MaxHEVCLuma != "" {
value, parseErr := strconv.ParseUint(document.MaxHEVCLuma, 10, 64)
if parseErr != nil {
return ManagementInfo{}, ErrProviderMalformed
}
info.MaxLumaPixelsHEVC = value
info.HasMaxLumaPixelsHEVC = true
}
return info, nil
}
type RTSPResponse struct {
@@ -206,14 +230,20 @@ type ProviderTelemetry struct {
MediaDrops uint64
}
type ProviderMedia struct {
Payload []byte
ReceivedAt time.Time
EnqueuedAt time.Time
}
type Provider interface {
Start(context.Context, LaunchRequest) (ProviderSession, error)
}
type ProviderSession interface {
Ready(context.Context) error
Video() <-chan []byte
Audio() <-chan []byte
Video() <-chan ProviderMedia
Audio() <-chan ProviderMedia
Events() <-chan ProviderEvent
Input(context.Context, InputEvent) error
Feedback(context.Context, Feedback) error
@@ -227,7 +257,7 @@ type ProviderSession interface {
type ApolloBackend interface {
Management(context.Context, LaunchRequest) ([]byte, error)
Setup(context.Context, LaunchRequest) ([]byte, error)
Setup(context.Context, LaunchRequest, []byte) ([]byte, error)
Open(context.Context, LaunchRequest, RTSPResponse) (ProviderSession, error)
}
@@ -268,7 +298,7 @@ func (a *ApolloAdapter) Start(ctx context.Context, request LaunchRequest) (Provi
if request.ProviderIdentity != "" && info.Identity.UniqueID != expected.UniqueID {
return nil, ErrProviderIdentity
}
rawRTSP, err := a.backend.Setup(ctx, request)
rawRTSP, err := a.backend.Setup(ctx, request, management)
if err != nil {
return nil, err
}
@@ -352,7 +382,7 @@ func (f *FakeApollo) Management(context.Context, LaunchRequest) ([]byte, error)
return []byte(fmt.Sprintf("<root><unique_id>%s</unique_id><fingerprint>%s</fingerprint><not_before>%s</not_before><not_after>%s</not_after><name>fixture-apollo</name></root>", identity.UniqueID, identity.Fingerprint, f.config.Now.Add(-time.Hour).Format(time.RFC3339), f.config.Now.Add(time.Hour).Format(time.RFC3339))), nil
}
func (f *FakeApollo) Setup(context.Context, LaunchRequest) ([]byte, error) {
func (f *FakeApollo) Setup(context.Context, LaunchRequest, []byte) ([]byte, error) {
if f.config.Failure == FakeFailureMalformed {
return []byte("RTSP/1.0 200 OK\r\n\r\n"), nil
}
@@ -362,8 +392,8 @@ func (f *FakeApollo) Setup(context.Context, LaunchRequest) ([]byte, error) {
func (f *FakeApollo) Open(_ context.Context, request LaunchRequest, _ RTSPResponse) (ProviderSession, error) {
session := &fakeSession{
failure: f.config.Failure,
video: make(chan []byte, 16),
audio: make(chan []byte, 16),
video: make(chan ProviderMedia, 16),
audio: make(chan ProviderMedia, 16),
events: make(chan ProviderEvent, 16),
clipboardWrites: make(chan string, 1),
state: protocol.ProviderState{Version: "1", SessionID: request.SessionID, State: ProviderStateStarting, Channels: []string{"video", "audio", "input", "feedback"}},
@@ -405,8 +435,8 @@ func (f *FakeApollo) DisconnectProvider() {
type fakeSession struct {
mu sync.Mutex
failure FakeFailure
video chan []byte
audio chan []byte
video chan ProviderMedia
audio chan ProviderMedia
events chan ProviderEvent
state protocol.ProviderState
pressed map[string]struct{}
@@ -432,8 +462,8 @@ func (s *fakeSession) Ready(ctx context.Context) error {
return nil
}
func (s *fakeSession) Video() <-chan []byte { return s.video }
func (s *fakeSession) Audio() <-chan []byte { return s.audio }
func (s *fakeSession) Video() <-chan ProviderMedia { return s.video }
func (s *fakeSession) Audio() <-chan ProviderMedia { return s.audio }
func (s *fakeSession) Events() <-chan ProviderEvent { return s.events }
func (s *fakeSession) EmitEvent(event ProviderEvent) {
@@ -449,15 +479,17 @@ func (s *fakeSession) EmitVideo(payload []byte) {
if s.state.State == ProviderStateTerminating || s.state.State == ProviderStateTerminated || s.state.State == ProviderStateDisconnected {
return
}
now := time.Now()
media := ProviderMedia{Payload: append([]byte(nil), payload...), ReceivedAt: now, EnqueuedAt: now}
select {
case s.video <- append([]byte(nil), payload...):
case s.video <- media:
default:
select {
case <-s.video:
default:
}
select {
case s.video <- append([]byte(nil), payload...):
case s.video <- media:
default:
}
}
@@ -469,15 +501,17 @@ func (s *fakeSession) EmitAudio(payload []byte) {
if s.state.State == ProviderStateTerminating || s.state.State == ProviderStateTerminated || s.state.State == ProviderStateDisconnected {
return
}
now := time.Now()
media := ProviderMedia{Payload: append([]byte(nil), payload...), ReceivedAt: now, EnqueuedAt: now}
select {
case s.audio <- append([]byte(nil), payload...):
case s.audio <- media:
default:
select {
case <-s.audio:
default:
}
select {
case s.audio <- append([]byte(nil), payload...):
case s.audio <- media:
default:
}
}