mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
* refactor: move proxy block message rendering to presentation layer * refactor: introduce ui.ProxyPresenter with injected advisory source * feat: friendly ecosystem labels in proxy block messages * test: cover ecosystemLabel derivation * fix: address review feedback on block context assertions and empty reference line
120 lines
3.3 KiB
Go
120 lines
3.3 KiB
Go
package proxy
|
||
|
||
import (
|
||
"net/http"
|
||
"net/url"
|
||
"time"
|
||
|
||
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
||
)
|
||
|
||
// ResponseAction determines how the proxy should handle a request
|
||
type ResponseAction int
|
||
|
||
const (
|
||
// ActionAllow forwards the request unchanged
|
||
ActionAllow ResponseAction = iota
|
||
|
||
// ActionBlock blocks the request with an error response
|
||
ActionBlock
|
||
|
||
// ActionModifyRequest modifies the request before forwarding
|
||
ActionModifyRequest
|
||
|
||
// ActionModifyResponse modifies the response after receiving
|
||
ActionModifyResponse
|
||
)
|
||
|
||
// RequestContext provides request information to interceptors
|
||
// This is passed to ShouldIntercept and HandleRequest methods
|
||
type RequestContext struct {
|
||
URL *url.URL
|
||
Method string
|
||
Headers http.Header
|
||
|
||
// Body is not currently used by the interceptors, but it is here for future use
|
||
Body []byte
|
||
|
||
Hostname string
|
||
RequestID string
|
||
StartTime time.Time
|
||
|
||
// Interceptor can store custom data
|
||
Data map[string]interface{}
|
||
}
|
||
|
||
// BlockReason identifies why an interceptor blocked a request
|
||
type BlockReason int
|
||
|
||
const (
|
||
BlockReasonNone BlockReason = iota
|
||
BlockReasonMalware
|
||
BlockReasonUserDeclined
|
||
BlockReasonConfirmationFailed
|
||
BlockReasonDependencyCooldown
|
||
)
|
||
|
||
// BlockContext carries the structured facts of a block decision so a
|
||
// presentation layer can render the user-facing message. Interceptors
|
||
// populate it instead of composing message text themselves.
|
||
type BlockContext struct {
|
||
Ecosystem packagev1.Ecosystem
|
||
PackageName string
|
||
PackageVersion string
|
||
|
||
// For BlockReasonMalware and BlockReasonUserDeclined
|
||
MalwareSummary string
|
||
MalwareReferenceURL string
|
||
|
||
// For BlockReasonDependencyCooldown
|
||
CooldownDays int
|
||
CooldownDaysAgo int
|
||
CooldownDaysLeft int
|
||
}
|
||
|
||
// InterceptorResponse defines how the proxy should handle the request
|
||
type InterceptorResponse struct {
|
||
// Action to take
|
||
Action ResponseAction
|
||
|
||
// For Action = Block: why and what was blocked. The proxy renders the
|
||
// response body from these via ProxyConfig.BlockMessageRenderer.
|
||
BlockReason BlockReason
|
||
BlockContext *BlockContext
|
||
|
||
// BlockMessage overrides the rendered message when non-empty
|
||
BlockMessage string
|
||
BlockCode int
|
||
|
||
// For Action = ModifyRequest: modified headers/body
|
||
ModifiedHeaders http.Header
|
||
|
||
// ModifiedBody is not currently used by the interceptors, but it is here for future use
|
||
ModifiedBody []byte
|
||
|
||
// For Action = ModifyResponse: response modification function
|
||
ResponseModifier ResponseModifierFunc
|
||
}
|
||
|
||
// ResponseModifierFunc modifies HTTP response
|
||
// It receives the status code, headers, and body, and returns modified versions
|
||
type ResponseModifierFunc func(statusCode int, headers http.Header, body []byte) (int, http.Header, []byte, error)
|
||
|
||
// Interceptor processes HTTP/HTTPS requests and can modify or block them
|
||
type Interceptor interface {
|
||
// Name returns the interceptor name for logging
|
||
Name() string
|
||
|
||
// ShouldIntercept determines if this interceptor handles the given request
|
||
ShouldIntercept(ctx *RequestContext) bool
|
||
|
||
// HandleRequest processes the request and returns response action
|
||
// Called for each request matching ShouldIntercept
|
||
HandleRequest(ctx *RequestContext) (*InterceptorResponse, error)
|
||
}
|
||
|
||
// MITMDecider is optional; implement to control whether CONNECT requests are MITM’d.
|
||
type MITMDecider interface {
|
||
ShouldMITM(ctx *RequestContext) bool
|
||
}
|