brig docs

Security

Claims

On this page

Each row of the claims table quotes one security promise Brig makes and names the tests that defend it.

The quotes and section names come from docs/security.md in the Brig repository. Security on this site covers the same facts.

Automated check #

script/check-claims.sh reads the table on every pull request. It fails in two cases:

  • a quoted sentence is gone from the page
  • a named test no longer exists

make claims runs the same check after its self-test.

Row format #

A row has three cells: claim, section, and defences.

Claim. The claim cell quotes the page. The check matches only the part in double quotes. A note in parentheses after the quote says which part of the sentence the row covers.

Quote the whole sentence, through its period. A qualifier added to the sentence on the page then ends the match. The match ignores line breaks and runs of spaces.

Section. The section cell names the heading above the sentence. The quote must be under that heading or under one nested in it.

Table shape. Every line after the table's delimiter row is a row, up to the first blank line, with or without its outer pipes. These fail the check:

  • a line in the table that is not three cells
  • a pipe line outside the table

Defence tokens #

Each defence in the last column is one token.

Token What it names How the check finds it
go:TestName a Go test func TestName( in a _test.go file
smoke:<text> an assertion in script/smoke.sh its ok "<text>" line
vm:<check> a check in script/claims-vm.sh its vm_check <check> line

Any other token, a row with none, or text beside the tokens fails the check.

VM checks #

A vm check needs a booted sandbox, and CI has no runtime. CI resolves a vm row by name and lists it as not yet run.

Command What it tests
make claims-vm builds brig from this checkout and runs the checks against that binary. It runs where hull or nerdctl is on PATH, and skips where neither is.
script/claims-vm.sh run by hand, tests the brig on PATH unless BRIG names another
script/claims-vm.sh --self-test runs in CI, as described below

Run make claims-vm before a merge that touches the run path.

The self-test answers every check from a fake guest that leaks one thing at a time. Each check must fail on the leak it tests for. A few checks also run their real probes through a stub brig on the host, so a probe with no answer also fails.

The self-test proves that each check judges an answer correctly. Only a booted sandbox proves that the guest leaks nothing.

Claims table #

Claim Section Defended by
"Beyond those, the guest does not have your keychain, your SSH agent, your secret manager, or any other directory on the host." (your keychain) The boundary go:TestTheRunPathReadsNoKeychain vm:keychain-not-reachable vm:secret-service-not-reachable
"Beyond those, the guest does not have your keychain, your SSH agent, your secret manager, or any other directory on the host." (your secret manager) The boundary go:TestTheRunPathCannotReachTheImporter go:TestUnresolvedReferencesAreRejectedButOrdinaryURLsAreNot
"Beyond those, the guest does not have your keychain, your SSH agent, your secret manager, or any other directory on the host." (your SSH agent) The boundary vm:ssh-agent-not-forwarded vm:no-agent-socket
"Beyond those, the guest does not have your keychain, your SSH agent, your secret manager, or any other directory on the host." (any other host directory) The boundary vm:other-host-directory
"Forwarded values go into the runtime process's own environment, and only the variable name appears on its command line." Not in argv go:TestSplitEnvKeepsValuesOutOfArgv go:TestRunArgsKeepsSecretValuesOutOfArgv smoke:credential values reach the runtime, but never through argv smoke:argv names the variables only
"Nothing is written into the guest home from the host for this." Credentials smoke:no credential is written into the workspace
"So every host-side read and write Brig makes inside the guest home goes through an os.Root opened on it." Writing into the workspace go:TestMarkerWriteRefusesAPlantedSymlink go:TestSetupGitRefusesASymlinkedGitconfig
"A variable on the profile's deny list is refused, with the reason." Credentials go:TestDenyAppliesToRefdValues go:TestOffSpellingsDoNotForwardADeniedCredential smoke:the metered key is refused, and says why smoke:a denied key never reaches the guest env line of a run smoke:a denied key's value never reaches argv, even under BRIG_ENV_ARGV=1
"Inside Brig, the guest has your guest home mounted as its home, read-write." The boundary smoke:the workspace is mounted as the guest home vm:guest-home-read-write
"Name a project on the run line and that project is a second host directory, also mounted read-write, at /work/<name>." The boundary smoke:the project is mounted at /work/<basename> vm:project-at-work
"The guest gets only the credentials you deliver to it." What the agent can reach smoke:an undeclared ambient variable and its value reach no runtime argv or env line smoke:an undeclared ambient variable stays out under an override too smoke:the declared credential name reaches the guest
"--home pointed at a symlink is refused for the same reason, with the same kind of message, and is fixed by naming the real directory." Writing into the workspace go:TestWorkspaceStillRefusesASymlinkAtTheWorkspace go:TestSymlinkedWorkspaceRootIsRefused go:TestWorkspaceRefusesASymlinkedParentComponent
"The one case with no innocent reading is an image sitting under our registry whose signature does not verify. That is the case that stops." (an image under ghcr.io/brig-sh/) Guest images smoke:a bad signature on our own image is reported smoke:a bad signature stops the boot (exit 5) with no terminal to ask go:TestVerifyRefusesAFailedSignatureWithNoTerminal go:TestVerifyRefusesAFailedSignatureWithStdinOnDevNull
"A scheme:// value read from the environment is refused as an unresolved secret-manager reference." Credentials smoke:a secret-manager reference is not forwarded go:TestUnresolvedReferencesAreRejectedButOrdinaryURLsAreNot go:TestEnvRefsStillGetTheUnresolvedRefGuard
"The object cosign checked is the object that runs, and the success line names the digest rather than the tag it came from." (the object that runs) The digest, not the tag go:TestVerifyResolvesVerifiesAndPinsAMatchingDigest go:TestRunArgsBootsThePinnedDigest go:TestNerdctlBootsTheVerifiedDigest smoke:the verified digest is what hull was told to boot

Type a command, a flag or an error message.