security

package
v0.0.7 Latest Latest
Warning

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

Go to latest
Published: Oct 27, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package security is an executable specification of cloop's threat model.

The security guarantees this suite protects are stated in prose in the README and enforced in code across four packages that do not import each other: pkg/executor (the host-execution policy), pkg/secretbroker (scoped credential grants), pkg/executorstore (enrollment token persistence), and pkg/ui (the control plane that must never execute anything itself). Nothing in that arrangement fails loudly when a refactor quietly reconnects the UI to os/exec, widens a container's privileges, or lets an expired lease redeem. A guarantee that only exists in prose is a guarantee that regresses silently.

So these tests assert the properties directly, at the boundary a reviewer would check by hand: not "does this function work" but "is it still impossible to reach exec.Command from an HTTP handler". They live outside the packages they audit on purpose — an external test can only use the exported surface, which is the same surface an attacker and an embedder see, and it cannot be weakened by a change to package internals without that change being visible here.

This file implements the static reachability analysis behind guarantee (1). The rest of the suite is behavioral.

Index

Constants

View Source
const ExecutorBoundary = ModulePath + "/pkg/executor"

ExecutorBoundary is the one package tree permitted to spawn processes.

The whole architecture rests on this being the only door: a driver under pkg/executor knows *where* a workload runs (this host, a container, a remote agent) and is the only code entitled to decide. Everything upstream of it speaks in Specs and Handles. Traversal stops here rather than treating it as a violation, which is what makes this a boundary check and not a ban.

View Source
const ModulePath = "github.com/blechschmidt/cloop"

ModulePath is the import path prefix of this module. Everything outside it is a dependency we do not audit.

Variables

View Source
var AuditedRoots = []string{
	ModulePath + "/pkg/ui",
	ModulePath + "/pkg/apiserver",
}

AuditedRoots are the packages whose HTTP handlers must not reach process execution. Both terminate network requests from potentially untrusted callers, so both inherit the same obligation.

View Source
var BackgroundRoots = []string{
	"github.com/blechschmidt/cloop/pkg/ui.NewInCluster",
	"(*github.com/blechschmidt/cloop/pkg/ui.Server).Run",
	"(*github.com/blechschmidt/cloop/pkg/ui.Server).Start",
	"(*github.com/blechschmidt/cloop/pkg/apiserver.Server).Run",
	"(*github.com/blechschmidt/cloop/pkg/apiserver.Server).Start",
}

BackgroundRoots are where the hub runs code no request asked for (Task 20418): its construction — which bootstraps the executors and runs the startup sweep — and Run and Start, which start the watchers, the cluster bus and the periodic sweep. Every goroutine the hub starts outside a request is referenced from one of them, and the reference graph follows a function value as an edge, so these reach all of it. A goroutine a handler starts is the handler's, and the handler check covers it.

Functions

This section is empty.

Types

type Graph

type Graph struct {
	// contains filtered or unexported fields
}

Graph is a whole-module function reference graph.

func LoadGraph

func LoadGraph() (*Graph, error)

LoadGraph type-checks every package in the module and builds the reference graph. Each call returns a graph of its own, so a test may seed edges into it without another test seeing them.

It deliberately loads the whole module rather than just the audited packages: the interesting violations are transitive, hiding two or three hops away in a helper package that shells out to git, and a check that only reads pkg/ui would never see them.

func (*Graph) FindBackgroundSpawns added in v0.0.5

func (g *Graph) FindBackgroundSpawns(doors map[string]string) (spawns []Violation, reached map[string]bool)

FindBackgroundSpawns returns every process the background roots reach outside the executor boundary, one Violation per spawning function, and the doors it stopped at.

It differs from FindSpawnReachable in three ways. Every spawn is reported, not the first per root: a root here is the whole hub, and one violation must not hide the next. The search stops at an HTTP handler, which the handler check owns. And it stops at doors — functions that run one program on the hub deliberately, each with the reason it is allowed and a test proving the reason (sanctionedHostDoors in callgraph_test.go); a door the search never reaches is stale.

func (*Graph) FindFunc

func (g *Graph) FindFunc(pkgPath, name string) (string, bool)

FindFunc returns the graph key for a function declared in pkgPath.

name may be a plain function ("forget") or a method ("(*Agent).forget" is matched by "forget" too, since a package rarely declares both). The lookup is by suffix rather than by an assembled FullName because the receiver's rendering — pointer, alias, type parameters — is a detail a test should not have to reproduce exactly.

func (*Graph) FindSpawnReachable

func (g *Graph) FindSpawnReachable() []Violation

FindSpawnReachable returns every audited handler that transitively reaches process execution outside the executor boundary.

Search is breadth-first so the reported path is the shortest one, which is almost always the one that explains the violation best. Packages under pkg/executor terminate the search: reaching a driver is the intended architecture, and the drivers spawn processes because that is their job.

func (*Graph) HandlerCount

func (g *Graph) HandlerCount() int

HandlerCount reports how many HTTP handlers were discovered, so a test can refuse to pass vacuously when a refactor renames or moves them all.

func (*Graph) MissingBackgroundRoots added in v0.0.5

func (g *Graph) MissingBackgroundRoots() []string

MissingBackgroundRoots lists the roots the graph does not have, so a test can refuse to pass when a rename leaves the background check with nothing to start from.

func (*Graph) Reaches

func (g *Graph) Reaches(from, to string) ([]string, bool)

Reaches reports whether `to` is reachable from `from`, returning the path.

Same reference-graph semantics as FindSpawnReachable: taking a function's address counts as an edge. For a "the wipe is reachable" assertion that over-approximates in the *unsafe* direction, so the path is returned for the test to print and a human to sanity-check rather than trusted blindly.

type Violation

type Violation struct {
	Handler string
	// Path is the chain of functions from handler to spawn, inclusive.
	Path []string
	// Spawn is the display name of the offending stdlib call.
	Spawn string
}

Violation is one HTTP handler that can reach a process-spawning call without passing through the executor boundary.

func (Violation) String

func (v Violation) String() string

String renders the violation as the path a reviewer needs to see. Printing the chain rather than just the endpoints is the difference between a test that reports a problem and a test that helps fix one — the fix is almost always at an intermediate hop, not at either end.

Jump to

Keyboard shortcuts

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