Documentation
¶
Overview ¶
Package writegate decides every write a browser sends: it lets the write through, or blocks it and keeps what it would have sent. A write is any request whose method is not GET, HEAD, or OPTIONS, and any request a policy declares a write.
step or session ── loopback HTTP + token ──▶ write gate process
│ DevTools Fetch on every request
▼
browser
The gate runs as a process of its own, beside the browser it guards, and answers each paused request at once: a request it held would be sent the moment its connection to the browser ended. It arms itself on the browser target before any page loads, so pages, frames, popups, workers, and service workers opened later are covered without arming each one. When its connection to a browser that still runs is lost, it closes that browser.
Index ¶
- Constants
- func Allowed(points []CommitPoint, key string) []string
- func Begin(ctx context.Context, handleURL string, signatures []string, ...) error
- func End(ctx context.Context, handleURL string) error
- func IsWrite(method string) bool
- func Main(ctx context.Context, stdin io.Reader, stdout io.WriteCloser) error
- func ParseMode(text string) (string, error)
- func Sends(mode string) bool
- func Serve(ctx context.Context, cfg Config, ready func(Ready)) error
- func SetPerson(ctx context.Context, handleURL string, on bool) error
- func Stricter(a, b string) string
- type CommitPoint
- type CommitStore
- func (s *CommitStore) Approve(ctx context.Context, dag, step string, from []CommitPoint, ...) ([]CommitPoint, error)
- func (s *CommitStore) Load(dag, step string) ([]CommitPoint, error)
- func (s *CommitStore) Note(ctx context.Context, dag, step string, seen ...CommitPoint) error
- func (s *CommitStore) Revoke(ctx context.Context, dag, step string, match func(CommitPoint) bool) ([]CommitPoint, error)
- func (s *CommitStore) Steps(dag string) ([]string, error)
- type Config
- type Event
- type Handle
- type Policy
- type Ready
- type Rule
Constants ¶
const ( // ModeOff runs no gate. ModeOff = "off" // ModeRecord sends only the writes the operation running may send, as // its approved commit points name them, and keeps each with its // answer; it blocks the others. ModeRecord = "record" // ModeAsk decides writes as ModeRecord does, and a step running in it // asks a person before each operation that is a commit point. ModeAsk = "ask" // ModeRehearse blocks every write and keeps what it would have sent. ModeRehearse = "rehearse" )
Modes, from the most permissive. A browser runs in the stricter of the mode its step or session asks for and the host's floor.
const ( // KindWrite is a write the gate decided. KindWrite = "write" // KindSocket is a WebSocket a service worker or shared worker opened, // whose writes the gate cannot see. KindSocket = "socket" )
Kinds of Event.
const ( // ClassWrite is a write of the work, one the gate exists for. ClassWrite = "write" // ClassApproved is a write of the work the operation running may send. ClassApproved = "approved" // ClassSignIn is a write to a sign-in endpoint the policy lists. ClassSignIn = "sign_in" // ClassNoise is a beacon, a report, a write to a noise host, or one to // an endpoint that only collects analytics or error reports. ClassNoise = "noise" // ClassPerson is a write sent while a person holds the browser. ClassPerson = "person" )
Classes of a write.
const ( OutcomeBlocked = "blocked" OutcomeSent = "sent" )
Outcomes of a write.
const (
// DefaultBodyLimit is how much of a write's body the gate keeps.
DefaultBodyLimit = 64 << 10
)
Variables ¶
This section is empty.
Functions ¶
func Allowed ¶
func Allowed(points []CommitPoint, key string) []string
Allowed returns the signatures the approved points of an operation send.
func Begin ¶
Begin tells the gate at handleURL which writes the operation starting may send, for at most lease. In record mode every other write is blocked, and so is every write once End is called or the lease ends.
func Main ¶
Main is the write-gate command. It reads a Config as JSON from stdin, writes one line to stdout once the gate decides the browser's requests, closes stdout, and serves until the browser is gone or the process is told to stop.
func Serve ¶
Serve guards the browser at cfg.CDPURL until the browser is gone or ctx ends. It calls ready once every request the browser makes is decided, before any page loads. Losing the browser's connection while the browser still runs closes the browser, as does ctx ending.
Types ¶
type CommitPoint ¶
type CommitPoint struct {
// Key identifies the operation as the step's replay cache does: its
// position, words, and page.
Key string `json:"key"`
// Op is the operation's position in with.do, Operation its words, and
// Page the page it ran on.
Op int `json:"op"`
Operation string `json:"operation"`
Page string `json:"page,omitempty"`
Signature string `json:"signature"`
Approved bool `json:"approved"`
// Actions are what the operation did on the page, recorded as the
// browser runtime replays them; an approved commit act replays them
// and asks no model.
Actions json.RawMessage `json:"actions,omitempty"`
// Example is the write as it was last seen.
Example *Event `json:"example,omitempty"`
SeenAt time.Time `json:"seen_at"`
ApprovedAt *time.Time `json:"approved_at,omitempty"`
}
CommitPoint is an operation of a step that sends a write, and the kind of write it sends. A rehearsal or a blocked write notes one; once approved, a step running in record mode sends that write from that operation and no other.
type CommitStore ¶
type CommitStore struct {
// contains filtered or unexported fields
}
CommitStore keeps the commit points of each DAG's steps under the browser data directory, apart from the replay cache, which a run may drop.
func NewCommitStore ¶
func NewCommitStore(browserDataDir string) *CommitStore
NewCommitStore returns the commit points kept under the browser data directory.
func (*CommitStore) Approve ¶
func (s *CommitStore) Approve(ctx context.Context, dag, step string, from []CommitPoint, match func(CommitPoint) bool, now time.Time) ([]CommitPoint, error)
Approve marks the points match accepts approved, adding those of from that are not kept yet, and returns them.
func (*CommitStore) Load ¶
func (s *CommitStore) Load(dag, step string) ([]CommitPoint, error)
Load returns the commit points of a step; none when it has no file.
func (*CommitStore) Note ¶
func (s *CommitStore) Note(ctx context.Context, dag, step string, seen ...CommitPoint) error
Note keeps the commit points seen, merged with those kept: a point already kept, by key and signature, keeps its approval and takes the newer example and actions.
func (*CommitStore) Revoke ¶
func (s *CommitStore) Revoke(ctx context.Context, dag, step string, match func(CommitPoint) bool) ([]CommitPoint, error)
Revoke withdraws the approval of the points match accepts and returns them.
type Config ¶
type Config struct {
// Token is a random value that prefixes the control paths.
Token string `json:"token"`
// CDPURL is the DevTools address of the browser, such as
// http://127.0.0.1:9333.
CDPURL string `json:"cdp_url"`
Mode string `json:"mode"`
Policy Policy `json:"policy"`
// Secrets holds the values of the secrets the policy's rules name, by
// name.
Secrets map[string]string `json:"secrets,omitempty"`
// ExtensionDir is where the socket-blocking extension is written.
ExtensionDir string `json:"extension_dir"`
// BodyLimit is how much of a write's body the gate keeps; zero means
// DefaultBodyLimit.
BodyLimit int `json:"body_limit,omitempty"`
}
Config is what a gate decides, and for which browser.
type Event ¶
type Event struct {
Seq int64 `json:"seq"`
At time.Time `json:"at"`
Kind string `json:"kind"`
// Method and URL are the request's, with sensitive query values
// hidden.
Method string `json:"method,omitempty"`
URL string `json:"url"`
ResourceType string `json:"resource_type,omitempty"`
Class string `json:"class,omitempty"`
Outcome string `json:"outcome,omitempty"`
// Signature identifies the kind of write across runs, apart from the
// values it carries.
Signature string `json:"signature,omitempty"`
ContentType string `json:"content_type,omitempty"`
// Fields are the names of the body's fields.
Fields []string `json:"fields,omitempty"`
// Body is the start of the body, with sensitive fields hidden.
Body string `json:"body,omitempty"`
// BodyBytes is the size of the whole body as the browser reported it.
BodyBytes int64 `json:"body_bytes,omitempty"`
// Target is the kind of worker that opened a socket.
Target string `json:"target,omitempty"`
// Status is the HTTP status a sent write was answered with, and
// Response the start of the answer, with sensitive fields hidden.
// ResponseError is the network error a sent write got in place of an
// answer.
Status int `json:"status,omitempty"`
Response string `json:"response,omitempty"`
ResponseError string `json:"response_error,omitempty"`
}
Event is a write the gate decided, or a socket it saw open.
func Events ¶
Events returns the events the gate at handleURL recorded after seq. An error means the gate no longer answers.
func (Event) Consequential ¶
Consequential reports whether the event is a write of the work the gate kept from being sent.
func (Event) String ¶
String describes the event in a few words, such as "POST https://example.com/deals".
type Handle ¶
type Handle struct {
// URL is the base of the gate's control paths.
URL string `json:"url"`
PID int `json:"pid"`
// StartedAt is the process's start time in Unix milliseconds, which
// tells it apart from a later process reusing PID.
StartedAt int64 `json:"started_at"`
}
Handle locates a running gate process.
func Start ¶
Start runs executable's browser write-gate command with cfg and returns once the gate decides every request of the browser at cfg.CDPURL. Start it before the browser loads any page. The gate outlives the caller and exits once the browser is gone.
type Policy ¶
type Policy struct {
// SignIn are the sign-in endpoints whose writes are sent.
SignIn []Rule `json:"sign_in,omitempty" yaml:"sign_in,omitempty"`
// Writes declare requests that save although their method only reads,
// such as a delete link.
Writes []Rule `json:"writes,omitempty" yaml:"writes,omitempty"`
// NoiseHosts are hosts whose writes, such as analytics, are blocked
// without counting as the work's writes.
NoiseHosts []string `json:"noise_hosts,omitempty" yaml:"noise_hosts,omitempty"`
}
Policy names the writes a gate treats apart from the others.
type Ready ¶
type Ready struct {
// Port is the port of the control paths.
Port int `json:"port"`
}
Ready describes a gate that decides the browser's requests.
type Rule ¶
type Rule struct {
// Host is a host pattern, as the hostpattern package matches it.
Host string `json:"host" yaml:"host"`
// Path is the request path; one ending in * matches every path it
// starts. Empty matches every path.
Path string `json:"path,omitempty" yaml:"path,omitempty"`
// Secret names a secret whose value the request must carry, as a
// sign-in carries its password.
Secret string `json:"secret,omitempty" yaml:"secret,omitempty"`
}
Rule matches requests by host and path.