From a469688c7d27f4e3af954593cd5ff3455ee8f648 Mon Sep 17 00:00:00 2001 From: Joern Barthel Date: Tue, 26 Jul 2022 18:26:24 +0200 Subject: [PATCH] feat: added e2e integration test --- .github/workflows/go.yml | 6 ++ go.mod | 8 ++ go.sum | 26 ++++++ offkey.go | 21 +++-- offkey_test.go | 171 +++++++++++++++++++++++++++++++++++++++ server/index.html | 4 +- 6 files changed, 227 insertions(+), 9 deletions(-) create mode 100644 offkey_test.go diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 4d990cf..953687c 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -21,5 +21,11 @@ jobs: - name: Build run: go build -v ./... + - name: Setup integration testing environment + run: | + sudo apt-get -y install expect zbar-tools + go run github.com/playwright-community/playwright-go/cmd/playwright install --with-deps + go install filippo.io/age/cmd/...@latest + - name: Test run: go test -v ./... diff --git a/go.mod b/go.mod index b738625..56752e7 100644 --- a/go.mod +++ b/go.mod @@ -9,9 +9,17 @@ require ( github.com/boombuler/barcode v1.0.1 github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 github.com/pkg/errors v0.9.1 + github.com/playwright-community/playwright-go v0.2000.1 + github.com/stretchr/testify v1.8.0 ) require ( + github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-stack/stack v1.8.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect golang.org/x/sys v0.0.0-20210903071746-97244b99971b // indirect + gopkg.in/square/go-jose.v2 v2.6.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index f84d883..9acfa6e 100644 --- a/go.sum +++ b/go.sum @@ -9,11 +9,30 @@ github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyR github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= +github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/playwright-community/playwright-go v0.2000.1 h1:2JViSHpJQ/UL/PO1Gg6gXV5IcXAAsoBJ3KG9L3wKXto= +github.com/playwright-community/playwright-go v0.2000.1/go.mod h1:1y9cM9b9dVHnuRWzED1KLM7FtbwTJC8ibDjI6MNqewU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -41,4 +60,11 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= diff --git a/offkey.go b/offkey.go index ea5978e..f94772e 100644 --- a/offkey.go +++ b/offkey.go @@ -11,18 +11,18 @@ import ( "github.com/yawn/offkey/server" ) -var fDescription string +var ( + fDescription string + fOpen bool +) -func init() { +func main() { + flag.BoolVar(&fOpen, "o", true, "try to open URL in browser automatically") flag.StringVar(&fDescription, "d", "", "a description of your secret") flag.Parse() -} - -func main() { - secret, err := ioutil.ReadAll(os.Stdin) if err != nil { @@ -37,7 +37,14 @@ func main() { os.Exit(1) } - if err := browser.OpenURL(s.URL()); err != nil { + if fOpen { + + if err := browser.OpenURL(s.URL()); err != nil { + fmt.Println(err) + os.Exit(1) + } + + } else { fmt.Printf("Open %q in your browser\n", s.URL()) } diff --git a/offkey_test.go b/offkey_test.go new file mode 100644 index 0000000..d792b5f --- /dev/null +++ b/offkey_test.go @@ -0,0 +1,171 @@ +package main_test + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "log" + "os/exec" + "regexp" + "strings" + "testing" + "time" + + "github.com/playwright-community/playwright-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestE2E(t *testing.T) { + + assert := assert.New(t) + require := require.New(t) + + // encrypt with offkey + + secret := fmt.Sprintf("this is my very secret message from %d", time.Now().Unix()) + + log.Printf("encrypting secret %q", secret) + + cmd := exec.Command( + "sh", + "-c", + fmt.Sprintf("echo %s | go run offkey.go -o=false", secret), + ) + + url := make(chan string) + + stdout, err := cmd.StdoutPipe() + require.NoError(err) + + go func(r io.ReadCloser, url chan string) { + + var ( + buf = make([]byte, 1024) + matcher = regexp.MustCompile(`(?m)Open "(.+)"`) + ) + + n, err := r.Read(buf) + require.NoError((err)) + + matches := matcher.FindStringSubmatch(string(buf[:n])) + assert.Len(matches, 2) + + url <- matches[1] + + }(stdout, url) + + go func() { + require.NoError(cmd.Run()) + }() + + // navigate to browser + + target := <-url + + pw, err := playwright.Run() + require.NoError(err) + + defer pw.Stop() + + browser, err := pw.Chromium.Launch() + require.NoError(err) + + defer browser.Close() + + page, err := browser.NewPage() + require.NoError(err) + + _, err = page.Goto(target) + require.NoError(err) + + // extract passphrase and search for the hint + + var passphrase string + + pp, err := page.QuerySelector("#passphrase") + assert.NoError(err) + assert.True(pp.IsVisible()) + + text, err := pp.InnerText() + assert.NoError(err) + assert.Regexp("(?:[a-z]+-){9}[a-z]+", text) + + passphrase = text + + log.Printf("using passphrase %q", passphrase) + + pp, err = page.QuerySelector("#passphrase-hint") + assert.NoError(err) + assert.True(pp.IsHidden()) + + // print the document, search for the passphrase and extract the hint + + page.EmulateMedia(playwright.PageEmulateMediaOptions{ + Media: playwright.MediaPrint, + }) + + pp, err = page.QuerySelector("#passphrase") + assert.NoError(err) + assert.True(pp.IsHidden()) + + pp, err = page.QuerySelector("#passphrase-hint") + assert.NoError(err) + assert.True(pp.IsVisible()) + + // extract qr code + + code, err := page.Screenshot() + require.NoError(err) + + err = ioutil.WriteFile("secret.png", code, 0666) + require.NoError(err) + + err = exec.Command( + "sh", + "-c", + "zbarimg --raw -q secret.png > secret.age", + ).Run() + + require.NoError(err) + + // decrypt with age + + out, err := exec.Command( + "sh", + "-c", + fmt.Sprintf(`expect -c 'spawn age --decrypt secret.age; expect -- "Enter passphrase:*"; send -- "%s\n"; expect eof'`, passphrase), + ).CombinedOutput() + assert.NoError(err) + + var ( + buf = bytes.NewBuffer(out) + last string + ) + + for { + + line, err := buf.ReadString('\n') + + if err != nil { + + if err == io.EOF { + break + } else { + require.NoError(err) + } + + } + + last = line + last = strings.TrimSuffix(last, "\n") + last = strings.TrimSuffix(last, "\r") + + } + + assert.Equal(secret, last) + + log.Printf("decrypted secret %q", last) + +} diff --git a/server/index.html b/server/index.html index 89cd97f..c03e85d 100644 --- a/server/index.html +++ b/server/index.html @@ -26,10 +26,10 @@
This QR code contains an age encrypted (https://github.com/FiloSottile/age) secret.
{{ .Code }}
-
This is passphrase to decrypt the age secret - it consists out of ten, dash separated, lower-case words.
+
This is passphrase to decrypt the age secret - it consists out of ten, dash separated, lower-case words.
This is passphrase to decrypt the age secret - write it down manually on the printout. -
{{ .Passphrase }}
+
{{ .Passphrase }}