Help us shape the future of Go! Take the 2026 Go Developer Survey

security

package
v0.4.2 Latest Latest
Warning

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

Go to latest
Published: Sep 30, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Overview

Package security enforces process-wide rules for authenticated identity, guarded HTTP, validated external names, and protected local files. Subsystems retain their domain types and compose these mechanisms instead of duplicating checks. Classified errors preserve the wire contract; unclassified errors remain opaque, and Redact bounds any external failure a caller elects to preserve.

Outbound HTTP primitives shared by external-service callers. UserAgent identifies the binary to the services it calls; the composition root sets it once at startup. A GuardedClient follows only same-origin redirects, so the Authorization header Go preserves across such a hop never reaches another host.

Private-file primitives shared by every subsystem that persists state or secrets. Directories must belong to this user at mode 0700; reads and process locks refuse links, loose modes, and foreign ownership. Replacement writes and syncs a mode-0600 temporary file before an atomic rename, then syncs the containing directory. Every path is verified when used, never trusted by its history.

Origins is the one browser-origin policy the OAuth boundary and the session routes apply. An origin is an exact HTTPS or loopback HTTP scheme and host. A request without Origin is a native client and passes; a present Origin must be allowlisted. Routes that serve only browsers additionally require Origin, which is the caller's decision, not this policy's.

Principal is the identity the OAuth boundary resolves and every subsystem scopes state by. It travels in the request context rather than in request or state structs. Shared name predicates also live here because callers validate identities and protected path segments without owning a common feature vocabulary.

Every cs-plane WebSocket negotiates one versioned subprotocol and takes its credential as a second, prefixed one, since a browser cannot set WebSocket headers. Upgrader admits any Origin because each route checks Origins first.

Index

Constants

View Source
const (
	MaxCredentialBytes = 16 << 10
)
View Source
const WebSocketProtocol = "cybershuttle.v1"

Variables

View Source
var ErrOriginNotAllowed = New("origin_not_allowed", "origin is not allowed", http.StatusForbidden)
View Source
var Upgrader = websocket.Upgrader{Subprotocols: []string{WebSocketProtocol}, CheckOrigin: func(*http.Request) bool { return true }}
View Source
var UserAgent = "cs-plane"

Functions

func AnswerAsPrincipal

func AnswerAsPrincipal[T any](status int, produce func(Principal, *http.Request) (T, error)) http.HandlerFunc

AnswerAsPrincipal, CreatedAsPrincipal and NoContentAsPrincipal are the three handler shapes every subsystem uses: a JSON answer, a 201 with Location, and a bodiless 204.

func BoundedClient

func BoundedClient(client *http.Client, fallback time.Duration) *http.Client

func CreatedAsPrincipal

func CreatedAsPrincipal[T any](location func(T) string, produce func(Principal, *http.Request) (T, error)) http.HandlerFunc

func DecodeBase64URL

func DecodeBase64URL(encoded string) ([]byte, bool)

func DecodeJSON

func DecodeJSON(request *http.Request, target any) error

func DecodeStrict

func DecodeStrict(r io.Reader, target any) error

func Do

func Do(client *http.Client, request *http.Request, limit int64) ([]byte, int, error)

func EnsurePrivateDir

func EnsurePrivateDir(dir string) error

func GetJSON

func GetJSON(ctx context.Context, client *http.Client, endpoint, authorization string, limit int64, destination any) error

func GuardedClient

func GuardedClient(client *http.Client, fallback time.Duration) *http.Client

func New

func New(code, message string, status int) error

func NewRequest

func NewRequest(ctx context.Context, method, endpoint, authorization string, body io.Reader) (*http.Request, error)

func NoContentAsPrincipal

func NoContentAsPrincipal(act func(Principal, *http.Request) error) http.HandlerFunc

func OpenLockFile

func OpenLockFile(path string) (*os.File, error)

OpenLockFile opens or creates a private lock file without following links; the caller flocks its descriptor.

func OwnedPrivate

func OwnedPrivate(info os.FileInfo) bool

OwnedPrivate reports whether info describes an entry this user owns with no group or world permission bits.

func PostForm

func PostForm(parent context.Context, client *http.Client, endpoint string, form url.Values, timeout time.Duration, limit int64) ([]byte, int, error)

func PrincipalDirName

func PrincipalDirName(principal Principal) string

func PrivateDir

func PrivateDir(path string) error

func ReadPrivateFile

func ReadPrivateFile(path string, limit int64) ([]byte, error)

func Redact

func Redact(operation string, err error, secrets ...string) error

func RemoveFile

func RemoveFile(path string) error

func ReplaceFile

func ReplaceFile(path string, data []byte) error

func SafeName

func SafeName(value string, max int) bool

func SameOriginRedirect

func SameOriginRedirect(from, to *url.URL) bool

func SubprotocolCredential added in v0.4.2

func SubprotocolCredential(protocols []string, prefix string) (string, bool)

SubprotocolCredential returns the prefixed credential from an offer of exactly it and WebSocketProtocol, any order.

func SyncDir

func SyncDir(path string) error

func TruncateUTF8

func TruncateUTF8(value string, limit int) string

func ValidCredential

func ValidCredential(value string) bool

func ValidIdentityValue

func ValidIdentityValue(value string) bool

func WithFileLock

func WithFileLock(path string, fn func() error) error

func WithPrincipal

func WithPrincipal(ctx context.Context, principal Principal) context.Context

func WriteError

func WriteError(writer http.ResponseWriter, err error)

func WriteJSON

func WriteJSON(writer http.ResponseWriter, status int, value any)

func WriteJSONBytes

func WriteJSONBytes(writer http.ResponseWriter, status int, body []byte)

Types

type APIError

type APIError struct {
	Code    string `json:"code"`
	Message string `json:"message"`
	Status  int    `json:"-"`
}

func For

func For(err error) *APIError

func (*APIError) Error

func (e *APIError) Error() string

type Envelope

type Envelope struct {
	Error *APIError `json:"error"`
}

type Origins

type Origins map[string]struct{}

func NewOrigins

func NewOrigins(allowed []string) (Origins, error)

func (Origins) Admits

func (o Origins) Admits(request *http.Request) bool

func (Origins) Allowed

func (o Origins) Allowed(origin string) bool

type Principal

type Principal struct {
	Subject string `json:"subject"`
	Tenant  string `json:"tenant"`
}

func PrincipalFromContext

func PrincipalFromContext(ctx context.Context) (Principal, error)

type SecretBox

type SecretBox [32]byte

func LoadOrCreateSecretBox

func LoadOrCreateSecretBox(path string) (*SecretBox, error)

func (*SecretBox) Open

func (b *SecretBox) Open(sealed []byte) ([]byte, error)

func (*SecretBox) Seal

func (b *SecretBox) Seal(plaintext []byte) ([]byte, error)

Jump to

Keyboard shortcuts

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