types

package
v0.1.19 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 14, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package types holds the proxy capture data model: the wire-fidelity HTTP/1.1 request/response types, the common Flow/Message envelope, and the rule-applier contract. It is depended on by the proxy package and the adapter registry (proxy/protocol) without forming an import cycle.

Index

Constants

View Source
const (
	ProtocolHTTP11 = "http/1.1"
	ProtocolH2     = "http/2"

	ProtocolTagWS      = "websocket"       // upgrade handshake flow
	ProtocolTagWSFrame = "websocket.frame" // per-frame child flow

	// AdapterScopeCore is the reserved scope naming the in-process proxy (HTTP/1.1,
	// HTTP/2, WebSocket) and Burp. It scopes rules and attributes core tools;
	// sidecars may not register under it.
	AdapterScopeCore = "sectool"

	SchemeHTTP  = "http"
	SchemeHTTPS = "https"

	DirectionC2S = "client_to_server"
	DirectionS2C = "server_to_client"

	// HeaderStreamID carries the wire stream id (e.g. an HTTP/2 stream id);
	// folded into request headers and skipped when serializing for display.
	HeaderStreamID = "X-Sectool-Stream-Id"

	// MethodFrame is the synthetic method used for WebSocket frame child flows.
	MethodFrame = "FRAME"
)

Protocol tags identify the protocol within a stored Flow.

Variables

This section is empty.

Functions

func EncodeStandardChunkedBody

func EncodeStandardChunkedBody(buf *bytes.Buffer, body, trailers []byte)

EncodeStandardChunkedBody writes body + trailers into buf as an HTTP/1.1 chunked body using CRLF terminators. trailers is written verbatim after the 0-chunk.

Types

type BodyCodec

type BodyCodec struct {
	Transforms  []string `json:"transforms,omitempty" msgpack:"tr,omitempty"`
	ContentType string   `json:"content_type,omitempty" msgpack:"ct,omitempty"`
}

BodyCodec is the transform chain and logical content-type mapping a Message's BodyRaw to its Body, supplied by adapters whose wire form sectool cannot natively decode.

type CertSpec added in v0.1.19

type CertSpec struct {
	DNSNames    []string
	IPAddresses []net.IP
	URIs        []*url.URL
	Emails      []string
	CommonName  string // upstream/declared CN; added as a SAN, not the leaf Subject.CN
}

CertSpec is additive leaf properties for a minted MITM leaf, beyond the hostname-derived single SAN. A nil *CertSpec yields the single-SAN leaf.

func (*CertSpec) Empty added in v0.1.19

func (s *CertSpec) Empty() bool

Empty reports whether the spec contributes no additive names.

type ChunkFrame

type ChunkFrame struct {
	// SizeLine is the raw size line without terminator, preserving extensions (e.g. "4;foo=bar") and hex casing.
	SizeLine []byte `json:"size_line,omitempty" msgpack:"sl,omitempty"`

	// SizeEnding is the terminator after the size line.
	SizeEnding LineEnding `json:"size_ending,omitempty" msgpack:"se,omitempty"`

	// Size is the chunk payload length in bytes (0 for final terminator chunk).
	Size int `json:"size,omitempty" msgpack:"sz,omitempty"`

	// DataEnding is the terminator after the chunk data. On the final 0-chunk it
	// is the trailer block's closing blank-line terminator, or EndingNone if truncated.
	DataEnding LineEnding `json:"data_ending,omitempty" msgpack:"de,omitempty"`

	// Malformed marks the trailing frame after a bad hex size; SizeLine holds
	// the raw bytes verbatim and the parser does not read past it.
	Malformed bool `json:"malformed,omitempty" msgpack:"mf,omitempty"`
}

ChunkFrame preserves per-chunk wire framing for chunked messages. Invariant: non-final Size values sum to len(Body); the final frame has Size=0.

type Flow

type Flow struct {
	// FlowID is the unique identifier, minted at Store time.
	FlowID string `json:"flow_id" msgpack:"fid"`

	// Adapter is the name of the adapter that emitted the flow.
	Adapter string `json:"adapter" msgpack:"ad"`

	// ProtocolTag is the protocol identifier within the adapter
	// (e.g. "http/1.1", "http/2", "websocket", "websocket.frame").
	ProtocolTag string `json:"protocol_tag" msgpack:"pr"`

	// Direction orients a one-way message: client_to_server, server_to_client,
	// or bidirectional. Empty for two-sided request/response flows.
	Direction string `json:"direction,omitempty" msgpack:"dir,omitempty"`

	// ParentFlowID links a child flow to its parent (e.g. a frame to its handshake).
	ParentFlowID string `json:"parent_flow_id,omitempty" msgpack:"pid,omitempty"`

	// Scheme is the captured request scheme ("http" or "https").
	Scheme string `json:"scheme,omitempty" msgpack:"sc,omitempty"`
	// Port is the captured upstream port.
	Port int `json:"port,omitempty" msgpack:"po,omitempty"`

	// Request and/or Response sides of the exchange.
	Request  *Message `json:"request,omitempty" msgpack:"rq,omitempty"`
	Response *Message `json:"response,omitempty" msgpack:"rs,omitempty"`

	// InterimResponses holds 1xx responses received before the final Response.
	InterimResponses []*Message `json:"interim_responses,omitempty" msgpack:"ir,omitempty"`

	// Timing metadata.
	StartedAt   time.Time `json:"started_at" msgpack:"ts"`
	CompletedAt time.Time `json:"completed_at,omitempty" msgpack:"ca,omitempty"`

	// Annotations is open-ended, exclusively sidecar-authored metadata.
	Annotations map[string]any `json:"annotations,omitempty" msgpack:"an,omitempty"`

	// InvokedBy names the sidecar that originated a cross-adapter message.
	InvokedBy string `json:"invoked_by,omitempty" msgpack:"ib,omitempty"`
	// SidecarInstanceID attributes a sidecar-emitted flow.
	SidecarInstanceID string `json:"sidecar_instance_id,omitempty" msgpack:"si,omitempty"`
}

Flow is the generalized store record for one logical exchange. It carries an optional request and response Message under a single flow_id. Child flows (e.g. WebSocket frames) reference a parent via ParentFlowID.

func (*Flow) ExtractMeta

func (f *Flow) ExtractMeta() HistoryMeta

ExtractMeta builds HistoryMeta from a Flow using its accessor methods.

func (*Flow) FormatInterimResponses

func (f *Flow) FormatInterimResponses(buf *bytes.Buffer) []string

FormatInterimResponses returns each 1xx response in wire form (HTTP/1.1 only).

func (*Flow) FormatRequest

func (f *Flow) FormatRequest(buf *bytes.Buffer) []byte

FormatRequest returns the request in wire-compatible format. For HTTP/1.1, uses SerializeRaw to preserve anomalies like bare-LF. For HTTP/2, rebuilds HTTP/1.1-style text from the folded pseudo-headers.

func (*Flow) FormatResponse

func (f *Flow) FormatResponse(buf *bytes.Buffer) []byte

FormatResponse returns the response in wire-compatible format. For HTTP/1.1, uses SerializeRaw to preserve anomalies like bare-LF. For HTTP/2, rebuilds HTTP/1.1-style text from the folded pseudo-headers.

func (*Flow) GetHost

func (f *Flow) GetHost() string

GetHost returns the request host.

func (*Flow) GetMethod

func (f *Flow) GetMethod() string

GetMethod returns the request method.

func (*Flow) GetPath

func (f *Flow) GetPath() string

GetPath returns the URL path without query string. For H2, strips the query portion since :path includes it.

func (*Flow) GetRequestHeader

func (f *Flow) GetRequestHeader(name string) string

GetRequestHeader returns a request header value (case-insensitive).

func (*Flow) GetResponseHeader

func (f *Flow) GetResponseHeader(name string) string

GetResponseHeader returns a response header value (case-insensitive).

func (*Flow) GetStatusCode

func (f *Flow) GetStatusCode() int

GetStatusCode returns the response status code.

type Header struct {
	// Name preserves original casing and whitespace anomalies
	// (e.g., "Content-Type", "content-type", or "Header " with trailing space)
	Name string `json:"name" msgpack:"n"`

	// Value is the header value with leading/trailing whitespace trimmed
	Value string `json:"value" msgpack:"v"`

	// RawLine contains the original wire bytes for this header (excluding line ending).
	// Used by SerializeRaw() to preserve exact wire format including obs-fold.
	// nil when header was programmatically created or Wire format not tracked.
	RawLine []byte `json:"raw_line,omitempty" msgpack:"rl,omitempty"`

	// LineEnding is the terminator observed for this header's final physical line.
	LineEnding LineEnding `json:"line_ending,omitempty" msgpack:"le,omitempty"`
}

Header represents a single HTTP header preserving original formatting.

type Headers

type Headers []Header

Headers is a slice of Header with helper methods for case-insensitive access. JSON serializes as an array, same as []Header.

func (*Headers) Get

func (h *Headers) Get(name string) string

Get returns the first header value with the given name (case-insensitive). Returns empty string if not found.

func (*Headers) Remove

func (h *Headers) Remove(name string)

Remove removes all headers with the given name (case-insensitive).

func (*Headers) Set

func (h *Headers) Set(name, value string)

Set sets or replaces the first header with the given name (case-insensitive). If not found, appends a new header. Clears RawLine since the programmatic value no longer matches the original wire bytes.

type HistoryMeta

type HistoryMeta struct {
	FlowID       string `msgpack:"fid"`
	Protocol     string `msgpack:"pr"`
	Adapter      string `msgpack:"ad,omitempty"`
	ParentFlowID string `msgpack:"pid,omitempty"`

	Scheme      string        `msgpack:"sc,omitempty"`
	Port        int           `msgpack:"po,omitempty"`
	Method      string        `msgpack:"m"`
	Host        string        `msgpack:"h"`
	Path        string        `msgpack:"p"` // includes query string
	Status      int           `msgpack:"s"`
	ContentType string        `msgpack:"ct"`
	RespLen     int           `msgpack:"rl"`
	Timestamp   time.Time     `msgpack:"ts"`
	Duration    time.Duration `msgpack:"d"`

	// Annotations carries the flow's sidecar-authored metadata.
	Annotations map[string]any `msgpack:"an,omitempty"`

	InvokedBy         string `msgpack:"ib,omitempty"`
	SidecarInstanceID string `msgpack:"si,omitempty"`
}

HistoryMeta holds lightweight metadata extracted at store time. Used by summary/list paths to avoid deserializing full request/response bodies.

type LineEnding

type LineEnding uint8

LineEnding identifies the terminator used on a single HTTP line. Zero value is EndingCRLF so unset fields emit the HTTP default.

const (
	EndingCRLF   LineEnding = 0 // "\r\n"
	EndingBareLF LineEnding = 1 // "\n"
	EndingBareCR LineEnding = 2 // "\r" - HTTP desync vector
	EndingNone   LineEnding = 3 // no terminator observed (EOF / truncation)
)

func (LineEnding) Bytes

func (e LineEnding) Bytes() string

Bytes returns the wire terminator for this line ending.

type Message

type Message struct {
	// Request-line fields (request side)
	Method string `json:"method,omitempty" msgpack:"m,omitempty"`
	Path   string `json:"path,omitempty" msgpack:"p,omitempty"`
	Query  string `json:"query,omitempty" msgpack:"q,omitempty"`

	// Version is "HTTP/1.1" / "HTTP/1.0" on either side.
	Version string `json:"version,omitempty" msgpack:"v,omitempty"`

	// Status-line fields (response side)
	StatusCode int    `json:"status_code,omitempty" msgpack:"sc,omitempty"`
	StatusText string `json:"status_text,omitempty" msgpack:"st,omitempty"`

	// Headers preserves order and original name casing/whitespace.
	Headers Headers `json:"headers" msgpack:"h"`

	// Body is the message body (decoded if chunked, raw otherwise).
	Body []byte `json:"body,omitempty" msgpack:"b,omitempty"`

	// BodyRaw holds the original wire bytes, set by an adapter only when they
	// differ from a decoded logical Body (protobuf, custom framing). nil
	// whenever Body already is the wire payload (HTTP and most flows). Replayed
	// verbatim when Body is unmutated.
	BodyRaw []byte `json:"body_raw,omitempty" msgpack:"br,omitempty"`

	// BodyCodec is the transform chain and logical content-type mapping BodyRaw
	// to Body, used to re-encode a mutated Body on replay. Set only with BodyRaw.
	BodyCodec *BodyCodec `json:"body_codec,omitempty" msgpack:"bc,omitempty"`

	// Trailers for chunked encoding (raw bytes).
	Trailers []byte `json:"trailers,omitempty" msgpack:"t,omitempty"`

	// Chunks preserves per-chunk wire framing for chunked messages.
	Chunks []ChunkFrame `json:"chunks,omitempty" msgpack:"ck,omitempty"`

	// FirstLineEnding is the terminator on the request-line or status-line.
	FirstLineEnding LineEnding `json:"first_line_ending,omitempty" msgpack:"fle,omitempty"`

	// HeaderBlockEnding is the terminator on the blank line ending the header block.
	HeaderBlockEnding LineEnding `json:"header_block_ending,omitempty" msgpack:"hbe,omitempty"`

	// Wire contains metadata about the original wire encoding.
	Wire *WireFormat `json:"wire,omitempty" msgpack:"w,omitempty"`

	// CloseDelimited indicates a response body framed by connection close.
	CloseDelimited bool `json:"-" msgpack:"-"`
}

Message is the common store envelope for one side of a Flow. It is the structural union of RawHTTP1Request and RawHTTP1Response: a request side leaves the status fields zero, a response side leaves method/path/query zero. Wire-fidelity fields carry over verbatim for HTTP/1.1; HTTP/2 folds its pseudo-headers into Headers (":method", ":status", etc).

func RequestToMessage

func RequestToMessage(r *RawHTTP1Request) *Message

RequestToMessage converts a parsed HTTP/1.1 request into a store Message.

func ResponseToMessage

func ResponseToMessage(r *RawHTTP1Response) *Message

ResponseToMessage converts a parsed HTTP/1.1 response into a store Message.

func (*Message) GetHeader

func (m *Message) GetHeader(name string) string

GetHeader returns the first header value with the given name (case-insensitive).

func (*Message) RemoveHeader

func (m *Message) RemoveHeader(name string)

RemoveHeader removes all headers with the given name (case-insensitive).

func (*Message) SetBody

func (m *Message) SetBody(b []byte)

SetBody replaces the body and clears chunk wire state.

func (*Message) SetHeader

func (m *Message) SetHeader(name, value string)

SetHeader sets or replaces the first header with the given name (case-insensitive).

type RawHTTP1Request

type RawHTTP1Request struct {
	// Request line components
	Method  string `json:"method" msgpack:"m"`                    // "GET", "POST", etc.
	Path    string `json:"path" msgpack:"p"`                      // path without query string, e.g., "/path"
	Query   string `json:"query,omitempty" msgpack:"q,omitempty"` // query string without leading ?, e.g., "foo=bar"
	Version string `json:"version" msgpack:"v"`                   // "HTTP/1.1" or "HTTP/1.0"

	// Headers preserves order and original name casing/whitespace
	Headers Headers `json:"headers" msgpack:"h"`

	// Body is the request body (decoded if chunked, raw otherwise)
	// For chunked encoding, this contains the reassembled body without chunk framing
	Body []byte `json:"body,omitempty" msgpack:"b,omitempty"`

	// Trailers for chunked encoding (raw bytes, rare but must preserve)
	// TODO - FUTURE - Parse trailers into []Header if trailer rules are needed
	Trailers []byte `json:"trailers,omitempty" msgpack:"t,omitempty"`

	// Chunks preserves per-chunk wire framing for chunked messages.
	// When body is mutated, callers must set to nil to avoid stale state being re-emitted.
	Chunks []ChunkFrame `json:"chunks,omitempty" msgpack:"ck,omitempty"`

	// Protocol metadata for replay fidelity
	Protocol string `json:"protocol" msgpack:"pr"` // "http/1.1" - stored for history/replay

	// RequestLineEnding is the terminator observed on the request line.
	RequestLineEnding LineEnding `json:"request_line_ending,omitempty" msgpack:"rle,omitempty"`

	// HeaderBlockEnding is the terminator observed on the blank line that ends the header block.
	HeaderBlockEnding LineEnding `json:"header_block_ending,omitempty" msgpack:"hbe,omitempty"`

	// Wire contains metadata about the original wire encoding.
	// Used by SerializeRaw() to preserve exact wire format.
	Wire *WireFormat `json:"wire,omitempty" msgpack:"w,omitempty"`
}

RawHTTP1Request represents a parsed HTTP/1.1 request with wire-level fidelity. The SerializeRaw() method reconstructs wire bytes from the stored components.

func (*RawHTTP1Request) Clone

func (r *RawHTTP1Request) Clone() *RawHTTP1Request

Clone returns a copy safe to mutate without affecting the receiver. Headers are deep-copied because in-place rule application mutates header elements; Body/Trailers/Chunks/Wire are reassigned wholesale by callers (never mutated in place), so sharing them is safe.

func (*RawHTTP1Request) GetHeader

func (r *RawHTTP1Request) GetHeader(name string) string

GetHeader returns the first header value with the given name (case-insensitive).

func (*RawHTTP1Request) RemoveHeader

func (r *RawHTTP1Request) RemoveHeader(name string)

RemoveHeader removes all headers with the given name (case-insensitive).

func (*RawHTTP1Request) SerializeRaw

func (r *RawHTTP1Request) SerializeRaw(buf *bytes.Buffer) []byte

SerializeRaw returns wire bytes reconstructed from the parsed request. Emits headers and body exactly as parsed; no auto-cleanup.

func (*RawHTTP1Request) SetBody

func (r *RawHTTP1Request) SetBody(b []byte)

SetBody replaces the body and clears wire state which can't be replicated with a body change.

func (*RawHTTP1Request) SetHeader

func (r *RawHTTP1Request) SetHeader(name, value string)

SetHeader sets or replaces the first header with the given name (case-insensitive).

type RawHTTP1Response

type RawHTTP1Response struct {
	// Status line components
	Version    string `json:"version" msgpack:"v"`                          // "HTTP/1.1" or "HTTP/1.0"
	StatusCode int    `json:"status_code" msgpack:"sc"`                     // 200, 404, etc.
	StatusText string `json:"status_text,omitempty" msgpack:"st,omitempty"` // "OK", "Not Found", etc.

	// Headers preserves order and original name casing
	Headers Headers `json:"headers" msgpack:"h"`

	// Body is the response body (decoded if chunked, raw otherwise)
	Body []byte `json:"body,omitempty" msgpack:"b,omitempty"`

	// Trailers for chunked encoding (raw bytes)
	// TODO - FUTURE - Parse trailers into []Header if trailer rules are needed
	Trailers []byte `json:"trailers,omitempty" msgpack:"t,omitempty"`

	// Chunks preserves per-chunk wire framing for chunked messages.
	// When body is mutated, callers must set to nil to avoid stale state being re-emitted.
	Chunks []ChunkFrame `json:"chunks,omitempty" msgpack:"ck,omitempty"`

	// StatusLineEnding is the terminator observed on the status line.
	StatusLineEnding LineEnding `json:"status_line_ending,omitempty" msgpack:"sle,omitempty"`

	// HeaderBlockEnding is the terminator observed on the blank line that ends the header block.
	HeaderBlockEnding LineEnding `json:"header_block_ending,omitempty" msgpack:"hbe,omitempty"`

	// Wire contains metadata about the original wire encoding.
	// Used by SerializeRaw() to preserve exact wire format.
	Wire *WireFormat `json:"wire,omitempty" msgpack:"w,omitempty"`

	// CloseDelimited indicates the body was framed by connection close (no Content-Length, not chunked).
	CloseDelimited bool `json:"-" msgpack:"-"`
}

RawHTTP1Response represents a parsed HTTP/1.1 response with wire-level fidelity.

func (*RawHTTP1Response) GetHeader

func (r *RawHTTP1Response) GetHeader(name string) string

GetHeader returns the first header value with the given name (case-insensitive).

func (*RawHTTP1Response) RemoveHeader

func (r *RawHTTP1Response) RemoveHeader(name string)

RemoveHeader removes all headers with the given name (case-insensitive).

func (*RawHTTP1Response) SerializeHeaders

func (r *RawHTTP1Response) SerializeHeaders(buf *bytes.Buffer) []byte

SerializeHeaders reconstructs the status line and headers only (no body). Useful for SendRequestResult where headers and body are returned separately.

func (*RawHTTP1Response) SerializeRaw

func (r *RawHTTP1Response) SerializeRaw(buf *bytes.Buffer) []byte

SerializeRaw returns wire bytes reconstructed from the parsed response. Emits headers and body exactly as parsed; no auto-cleanup.

func (*RawHTTP1Response) SetBody

func (r *RawHTTP1Response) SetBody(b []byte)

SetBody replaces the body and clears wire state which can't be replicated with a body change.

func (*RawHTTP1Response) SetHeader

func (r *RawHTTP1Response) SetHeader(name, value string)

SetHeader sets or replaces the first header with the given name (case-insensitive).

type RuleApplier

type RuleApplier interface {
	// ApplyRequestRules applies request header and body rules.
	// Returns the modified request (may be same instance if no changes).
	ApplyRequestRules(req *RawHTTP1Request) *RawHTTP1Request

	// ApplyResponseRules applies response header and body rules.
	// Handles decompression/recompression for body rules.
	ApplyResponseRules(resp *RawHTTP1Response) *RawHTTP1Response

	// ApplyRequestBodyOnlyRules applies only body rules to a request body.
	// Used by HTTP/2 where headers are sent separately before body.
	// Requires headers for Content-Encoding detection (compression-aware).
	// Does not apply header rules.
	// Returns error if recompression fails (caller should reset stream).
	ApplyRequestBodyOnlyRules(body []byte, headers Headers) ([]byte, error)

	// ApplyResponseBodyOnlyRules applies only body rules to a response body.
	// Used by HTTP/2 where headers are sent separately before body.
	// Requires headers for Content-Encoding detection (compression-aware).
	// Does not apply header rules.
	ApplyResponseBodyOnlyRules(body []byte, headers Headers) []byte

	// ApplyWSRules applies WebSocket rules to frame payload.
	// direction is "ws:to-server" or "ws:to-client".
	ApplyWSRules(payload []byte, direction string) []byte

	// HasBodyRules returns true if there are body rules for request or response.
	// Used by HTTP/2 handler to decide whether to buffer full bodies.
	// isRequest=true checks for request_body rules, false checks for response_body rules.
	HasBodyRules(isRequest bool) bool
}

RuleApplier applies find/replace rules to requests and responses. Implemented by the service layer (NativeProxyBackend). Rules are applied in the order they were added (list order).

type Target

type Target struct {
	Hostname  string
	Port      int
	UsesHTTPS bool
}

Target specifies where to send a request.

func (*Target) Scheme

func (t *Target) Scheme() string

Scheme returns "https" when UsesHTTPS, else "http".

type WireFormat

type WireFormat struct {
	// WasChunked indicates chunked transfer encoding was received.
	WasChunked bool `json:"was_chunked,omitempty" msgpack:"wc,omitempty"`

	// UsedBareLF is true when any line used bare LF (\n) as terminator.
	UsedBareLF bool `json:"used_bare_lf,omitempty" msgpack:"lf,omitempty"`

	// UsedBareCR is true when any line used bare CR (\r) as terminator.
	UsedBareCR bool `json:"used_bare_cr,omitempty" msgpack:"cr,omitempty"`
}

WireFormat stores summary metadata about the original wire encoding. Per-line terminators are tracked on the LineEnding fields; these flags are informational and drive chunked re-emission (UsedBareCR > UsedBareLF > CRLF).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL