344 lines
8.6 KiB
Go
344 lines
8.6 KiB
Go
|
|
package main
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"flag"
|
|||
|
|
"fmt"
|
|||
|
|
"os"
|
|||
|
|
"os/exec"
|
|||
|
|
"path/filepath"
|
|||
|
|
"testing"
|
|||
|
|
|
|||
|
|
"github.com/StackExchange/blackbox/v2/pkg/bblog"
|
|||
|
|
_ "github.com/StackExchange/blackbox/v2/pkg/bblog"
|
|||
|
|
_ "github.com/StackExchange/blackbox/v2/pkg/vcs/_all"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
var vcsToTest = flag.String("testvcs", "GIT", "VCS to test")
|
|||
|
|
var longTests = flag.Bool("long", false, "Run long version of tests")
|
|||
|
|
|
|||
|
|
//var crypterToTest = flag.String("crypter", "GnuPG", "crypter to test")
|
|||
|
|
|
|||
|
|
func init() {
|
|||
|
|
testing.Init()
|
|||
|
|
flag.Parse()
|
|||
|
|
|
|||
|
|
op, err := os.Getwd()
|
|||
|
|
if err != nil {
|
|||
|
|
panic(err)
|
|||
|
|
}
|
|||
|
|
originPath = op
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func compile(t *testing.T) {
|
|||
|
|
if PathToBlackBox() != "" {
|
|||
|
|
// It's been compiled already.
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
// Make sure we have the latest binary
|
|||
|
|
fmt.Println("========== Compiling")
|
|||
|
|
cmd := exec.Command("go", "build", "-o", "../bbintegration", "../cmd/blackbox")
|
|||
|
|
cmd.Stdout = os.Stdout
|
|||
|
|
cmd.Stderr = os.Stderr
|
|||
|
|
err := cmd.Run()
|
|||
|
|
if err != nil {
|
|||
|
|
t.Fatalf("setup_compile: %v", err)
|
|||
|
|
}
|
|||
|
|
cwd, err := os.Getwd()
|
|||
|
|
if err != nil {
|
|||
|
|
t.Fatal(err)
|
|||
|
|
}
|
|||
|
|
SetPathToBlackBox(filepath.Join(cwd, "../bbintegration"))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func setup(t *testing.T) {
|
|||
|
|
logDebug := bblog.GetDebug(*verbose)
|
|||
|
|
|
|||
|
|
logDebug.Printf("flag.testvcs is %v", *vcsToTest)
|
|||
|
|
vh := getVcs(t, *vcsToTest)
|
|||
|
|
logDebug.Printf("Using BLACKBOX_VCS=%v", vh.Name())
|
|||
|
|
os.Setenv("BLACKBOX_VCS", vh.Name())
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestInit(t *testing.T) {
|
|||
|
|
if !*longTests {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
compile(t)
|
|||
|
|
makeHomeDir(t, "init")
|
|||
|
|
|
|||
|
|
// Only zero or one args are permitted.
|
|||
|
|
invalidArgs(t, "init", "one", "two")
|
|||
|
|
invalidArgs(t, "init", "one", "two", "three")
|
|||
|
|
|
|||
|
|
runBB(t, "init", "yes")
|
|||
|
|
assertFileEmpty(t, ".blackbox/blackbox-admins.txt")
|
|||
|
|
assertFileEmpty(t, ".blackbox/blackbox-files.txt")
|
|||
|
|
assertFilePerms(t, ".blackbox/blackbox-admins.txt", 0o640)
|
|||
|
|
assertFilePerms(t, ".blackbox/blackbox-files.txt", 0o640)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestList(t *testing.T) {
|
|||
|
|
if !*longTests {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
compile(t)
|
|||
|
|
makeHomeDir(t, "init")
|
|||
|
|
|
|||
|
|
runBB(t, "init", "yes")
|
|||
|
|
createDummyFilesAdmin(t)
|
|||
|
|
checkOutput("000-admin-list.txt", t, "admin", "list")
|
|||
|
|
checkOutput("000-file-list.txt", t, "file", "list")
|
|||
|
|
|
|||
|
|
invalidArgs(t, "file", "list", "extra")
|
|||
|
|
invalidArgs(t, "admin", "list", "extra")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestStatus(t *testing.T) {
|
|||
|
|
if !*longTests {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
compile(t)
|
|||
|
|
makeHomeDir(t, "init")
|
|||
|
|
|
|||
|
|
runBB(t, "init", "yes")
|
|||
|
|
createFilesStatus(t)
|
|||
|
|
checkOutput("000-status.txt", t, "status")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestShred(t *testing.T) {
|
|||
|
|
if !*longTests {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
compile(t)
|
|||
|
|
makeHomeDir(t, "shred")
|
|||
|
|
runBB(t, "init", "yes")
|
|||
|
|
|
|||
|
|
makeFile(t, "shredme.txt", "File with SHREDME in it.\n")
|
|||
|
|
assertFileExists(t, "shredme.txt")
|
|||
|
|
runBB(t, "shred", "shredme.txt")
|
|||
|
|
assertFileMissing(t, "shredme.txt")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestStatus_notreg(t *testing.T) {
|
|||
|
|
if !*longTests {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
compile(t)
|
|||
|
|
makeHomeDir(t, "init")
|
|||
|
|
|
|||
|
|
runBB(t, "init", "yes")
|
|||
|
|
createFilesStatus(t)
|
|||
|
|
checkOutput("status-noreg.txt", t, "status", "status-ENCRYPTED.txt", "blah.txt")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TestHard tests the functions using a fake homedir and repo.
|
|||
|
|
func TestHard(t *testing.T) {
|
|||
|
|
if !*longTests {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
// These are basic tests that work on a fake repo.
|
|||
|
|
// The repo has mostly real data, except any .gpg file
|
|||
|
|
// is just garbage.
|
|||
|
|
compile(t)
|
|||
|
|
setup(t)
|
|||
|
|
|
|||
|
|
for _, cx := range []struct{ subname, prefix string }{
|
|||
|
|
//{subname: ".", prefix: "."},
|
|||
|
|
{subname: "mysub", prefix: ".."},
|
|||
|
|
} {
|
|||
|
|
subname := cx.subname
|
|||
|
|
prefix := cx.prefix
|
|||
|
|
_ = prefix
|
|||
|
|
|
|||
|
|
phase("========== SUBDIR = " + subname + " ==========")
|
|||
|
|
|
|||
|
|
makeHomeDir(t, "BasicAlice")
|
|||
|
|
|
|||
|
|
plaintextFoo := "I am the foo.txt file!\n"
|
|||
|
|
plainAltered := "I am the altered file!\n"
|
|||
|
|
|
|||
|
|
runBB(t, "testing_init") // Runs "git init" or equiv
|
|||
|
|
assertFileExists(t, ".git")
|
|||
|
|
runBB(t, "init", "yes") // Creates .blackbox or equiv
|
|||
|
|
|
|||
|
|
if subname != "." {
|
|||
|
|
err := os.Mkdir(subname, 0770)
|
|||
|
|
if err != nil {
|
|||
|
|
t.Fatal(fmt.Errorf("hard-mk-home %q: %v", subname, err))
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
olddir, err := os.Getwd()
|
|||
|
|
if err != nil {
|
|||
|
|
t.Fatal(err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
os.Chdir(subname)
|
|||
|
|
os.Chdir(olddir)
|
|||
|
|
|
|||
|
|
phase("Alice creates a GPG key")
|
|||
|
|
gpgdir := makeAdmin(t, "alice", "Alice Example", "alice@example.com")
|
|||
|
|
become(t, "alice")
|
|||
|
|
|
|||
|
|
phase("Alice enrolls as an admin")
|
|||
|
|
//os.Chdir(subname)
|
|||
|
|
runBB(t, "admin", "add", "alice@example.com", gpgdir)
|
|||
|
|
//os.Chdir(olddir)
|
|||
|
|
|
|||
|
|
// encrypt
|
|||
|
|
phase("Alice registers foo.txt")
|
|||
|
|
makeFile(t, "foo.txt", plaintextFoo)
|
|||
|
|
//os.Chdir(subname)
|
|||
|
|
//runBB(t, "file", "add", "--shred", filepath.Join(prefix, "foo.txt"))
|
|||
|
|
runBB(t, "file", "add", "--shred", "foo.txt")
|
|||
|
|
//os.Chdir(olddir)
|
|||
|
|
// "file add" encrypts the file.
|
|||
|
|
// We shred the plaintext so that we are sure that when Decrypt runs,
|
|||
|
|
// we can verify the contents wasn't just sitting there all the time.
|
|||
|
|
assertFileMissing(t, "foo.txt")
|
|||
|
|
assertFileExists(t, "foo.txt.gpg")
|
|||
|
|
|
|||
|
|
phase("Alice decrypts foo.txt")
|
|||
|
|
// decrypt
|
|||
|
|
//os.Chdir(subname)
|
|||
|
|
runBB(t, "decrypt", "foo.txt")
|
|||
|
|
//runBB(t, "decrypt", filepath.Join(prefix, "foo.txt"))
|
|||
|
|
//os.Chdir(olddir)
|
|||
|
|
assertFileExists(t, "foo.txt")
|
|||
|
|
assertFileExists(t, "foo.txt.gpg")
|
|||
|
|
assertFileContents(t, "foo.txt", plaintextFoo)
|
|||
|
|
|
|||
|
|
// encrypts (without shredding)
|
|||
|
|
phase("Alice encrypts foo.txt (again)")
|
|||
|
|
runBB(t, "encrypt", "foo.txt")
|
|||
|
|
assertFileExists(t, "foo.txt")
|
|||
|
|
assertFileExists(t, "foo.txt.gpg")
|
|||
|
|
assertFileContents(t, "foo.txt", plaintextFoo)
|
|||
|
|
|
|||
|
|
// reencrypt
|
|||
|
|
phase("Alice reencrypts")
|
|||
|
|
checkOutput("basic-status.txt", t, "status")
|
|||
|
|
runBB(t, "reencrypt", "--overwrite", "foo.txt")
|
|||
|
|
|
|||
|
|
// Test variations of cat
|
|||
|
|
|
|||
|
|
// foo.txt=plain result=plain
|
|||
|
|
phase("Alice cats plain:plain")
|
|||
|
|
makeFile(t, "foo.txt", plaintextFoo)
|
|||
|
|
assertFileExists(t, "foo.txt")
|
|||
|
|
runBB(t, "encrypt", "foo.txt")
|
|||
|
|
assertFileExists(t, "foo.txt")
|
|||
|
|
assertFileExists(t, "foo.txt.gpg")
|
|||
|
|
checkOutput("alice-cat-plain.txt", t, "cat", "foo.txt")
|
|||
|
|
assertFileExists(t, "foo.txt")
|
|||
|
|
assertFileExists(t, "foo.txt.gpg")
|
|||
|
|
|
|||
|
|
// foo.txt=altered result=plain
|
|||
|
|
phase("Alice cats altered:plain")
|
|||
|
|
makeFile(t, "foo.txt", plainAltered)
|
|||
|
|
assertFileExists(t, "foo.txt")
|
|||
|
|
assertFileExists(t, "foo.txt.gpg")
|
|||
|
|
checkOutput("alice-cat-plain.txt", t, "cat", "foo.txt")
|
|||
|
|
assertFileExists(t, "foo.txt")
|
|||
|
|
assertFileExists(t, "foo.txt.gpg")
|
|||
|
|
|
|||
|
|
// foo.txt=missing result=plain
|
|||
|
|
phase("Alice cats missing:plain")
|
|||
|
|
removeFile(t, "foo.txt")
|
|||
|
|
assertFileMissing(t, "foo.txt")
|
|||
|
|
assertFileMissing(t, "foo.txt")
|
|||
|
|
assertFileExists(t, "foo.txt.gpg")
|
|||
|
|
checkOutput("alice-cat-plain.txt", t, "cat", "foo.txt")
|
|||
|
|
assertFileMissing(t, "foo.txt")
|
|||
|
|
assertFileExists(t, "foo.txt.gpg")
|
|||
|
|
|
|||
|
|
// Chapter 2: Bob
|
|||
|
|
// Alice adds Bob.
|
|||
|
|
// Bob encrypts a file.
|
|||
|
|
// Bob makes sure he can decrypt alice's file.
|
|||
|
|
// Bob removes Alice.
|
|||
|
|
// Alice verifies she CAN'T decrypt files.
|
|||
|
|
// Bob adds Alice back.
|
|||
|
|
// Alice verifies she CAN decrypt files.
|
|||
|
|
// Bob adds an encrypted file by mistake, "bb add" and fixes it.
|
|||
|
|
// Bob corrupts the blackbox-admins.txt file, verifies that commands fail.
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TestEvilFilenames verifies commands work with "difficult" file names
|
|||
|
|
func TestEvilFilenames(t *testing.T) {
|
|||
|
|
if !*longTests {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
compile(t)
|
|||
|
|
setup(t)
|
|||
|
|
makeHomeDir(t, "Mallory")
|
|||
|
|
|
|||
|
|
runBB(t, "testing_init") // Runs "git init" or equiv
|
|||
|
|
assertFileExists(t, ".git")
|
|||
|
|
runBB(t, "init", "yes") // Creates .blackbox or equiv
|
|||
|
|
|
|||
|
|
phase("Malory creates a GPG key")
|
|||
|
|
gpgdir := makeAdmin(t, "mallory", "Mallory Evil", "mallory@example.com")
|
|||
|
|
become(t, "mallory")
|
|||
|
|
|
|||
|
|
phase("Mallory enrolls as an admin")
|
|||
|
|
runBB(t, "admin", "add", "mallory@example.com", gpgdir)
|
|||
|
|
|
|||
|
|
_ = os.MkdirAll("my/path/to", 0o770)
|
|||
|
|
_ = os.Mkdir("other", 0o770)
|
|||
|
|
|
|||
|
|
for i, name := range []string{
|
|||
|
|
"!important!.txt",
|
|||
|
|
"#andpounds.txt",
|
|||
|
|
"stars*bars?.txt",
|
|||
|
|
"space space.txt",
|
|||
|
|
"tab\ttab.txt",
|
|||
|
|
"ret\rret.txt",
|
|||
|
|
"smile😁eyes",
|
|||
|
|
"¡que!",
|
|||
|
|
"thé",
|
|||
|
|
"pound£",
|
|||
|
|
"*.go",
|
|||
|
|
"rm -f erase ; echo done",
|
|||
|
|
`smile☺`,
|
|||
|
|
`dub𝓦`,
|
|||
|
|
"my/path/to/relsecrets.txt",
|
|||
|
|
//"my/../my/path/../path/to/myother.txt", // Not permitted yet
|
|||
|
|
//"other/../my//path/../path/to/otherother.txt", // Not permitted yet
|
|||
|
|
//"new\nnew.txt", // \n not permitted
|
|||
|
|
//"two\n", // \n not permitted (yet)
|
|||
|
|
//"four\U0010FFFF", // Illegal byte sequence. git won't accept.
|
|||
|
|
} {
|
|||
|
|
phase(fmt.Sprintf("Mallory tries %02d: %q", i, name))
|
|||
|
|
contents := "the name of this file is the talking heads... i mean, " + name
|
|||
|
|
makeFile(t, name, contents)
|
|||
|
|
assertFileExists(t, name)
|
|||
|
|
assertFileMissing(t, name+".gpg")
|
|||
|
|
assertFileContents(t, name, contents)
|
|||
|
|
|
|||
|
|
runBB(t, "file", "add", name)
|
|||
|
|
assertFileMissing(t, name)
|
|||
|
|
assertFileExists(t, name+".gpg")
|
|||
|
|
|
|||
|
|
runBB(t, "decrypt", name)
|
|||
|
|
assertFileExists(t, name)
|
|||
|
|
assertFileExists(t, name+".gpg")
|
|||
|
|
assertFileContents(t, name, contents)
|
|||
|
|
|
|||
|
|
runBB(t, "encrypt", name)
|
|||
|
|
assertFileExists(t, name)
|
|||
|
|
assertFileExists(t, name+".gpg")
|
|||
|
|
assertFileContents(t, name, contents)
|
|||
|
|
|
|||
|
|
runBB(t, "shred", name)
|
|||
|
|
assertFileMissing(t, name)
|
|||
|
|
assertFileExists(t, name+".gpg")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// More tests to implement.
|
|||
|
|
// 1. Verify that the --gid works (blackbox decrypt --gid)
|