Skip to content
quarry Artifact [9e458553db]
Login

Artifact [9e458553db]

Artifact 9e458553db1c0c8145e16c172e45c33524218a4cf90d4059552eec11714e1370:


package web

import (
	"crypto/rand"
	"encoding/base64"
	"net/http"
	"net/http/httptest"
	"net/url"
	"path/filepath"
	"strings"
	"testing"

	"craftdesign.group/quarry/internal/api"
	"craftdesign.group/quarry/internal/cisecret"
	"craftdesign.group/quarry/internal/oltp"
	"craftdesign.group/quarry/internal/ops"
)

// THE ENVIRONMENTS SECTION MANAGES SECRETS WITHOUT EVER SHOWING ONE
// (RFC 0019): create an environment, save a secret into it and into the
// repository's own, see the names and never a value, delete them.
func TestEnvironmentsSectionNeverShowsAValue(t *testing.T) {
	db, err := oltp.Open(filepath.Join(t.TempDir(), "o.db"))
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { db.Close() })
	key := make([]byte, 32)
	rand.Read(key)
	box, _ := cisecret.New(base64.StdEncoding.EncodeToString(key))
	plane := &memCaps{stored: map[string]string{"dan": "a"}}
	r := ops.NewRegistry()
	if err := api.RegisterAll(r, api.Deps{Version: "v",
		Repos:   api.NewRepos(map[string]*api.Repo{"alpha": {Index: landingIndex(t, false)}}),
		Roster:  capsRoster{plane},
		Secrets: db, SecretBox: box}); err != nil {
		t.Fatal(err)
	}
	srv := httptest.NewServer(HandlerWith(r, nil, nil, []Option{
		WithSessions(fakeSessions{user: "dan", roles: map[string]ops.Auth{"alpha": ops.Admin}}),
		WithCaps(plane)}))
	t.Cleanup(srv.Close)

	body := asDan(t, srv, "/r/alpha/settings")
	if !strings.Contains(body, `id="environments"`) || !strings.Contains(body, "Repository secrets") || !strings.Contains(body, "${{ secrets.NAME }}") {
		t.Fatalf("FAIL: the settings page has no Environments section with the repository's own secrets")
	}
	post := func(form url.Values) {
		t.Helper()
		resp := postAsDan(t, srv, "/r/alpha/settings/environments", form)
		if resp.StatusCode != http.StatusSeeOther || !strings.HasSuffix(resp.Header.Get("Location"), "#environments") {
			t.Fatalf("FAIL: %v answered %d -> %s", form["action"], resp.StatusCode, resp.Header.Get("Location"))
		}
	}
	post(url.Values{"action": {"set-environment"}, "name": {"production"}, "branches": {""}})
	post(url.Values{"action": {"set-secret"}, "environment": {"production"}, "name": {"CF_TOKEN"}, "value": {"very-secret-value-1"}})
	post(url.Values{"action": {"set-secret"}, "environment": {""}, "name": {"SHARED"}, "value": {"very-secret-value-2"}})
	post(url.Values{"action": {"set-environment"}, "name": {"preview"}, "branches": {"dev, feat-*"}})

	body = asDan(t, srv, "/r/alpha/settings")
	if strings.Contains(body, "very-secret-value") {
		t.Fatal("FAIL: the settings page shows a secret's value")
	}
	for _, want := range []string{`data-hook="secret-name">CF_TOKEN<`, `data-hook="secret-name">SHARED<`, "protected branches only", "branches dev feat-*", `value="dev feat-*"`} {
		if !strings.Contains(body, want) {
			t.Errorf("FAIL: the settings page lacks %q", want)
		}
	}
	post(url.Values{"action": {"delete-secret"}, "environment": {"production"}, "name": {"CF_TOKEN"}})
	post(url.Values{"action": {"delete-environment"}, "name": {"preview"}})
	body = asDan(t, srv, "/r/alpha/settings")
	if strings.Contains(body, `>CF_TOKEN<`) || strings.Contains(body, "feat-*") {
		t.Error("FAIL: a deleted secret or environment is still listed")
	}
	log, _ := db.SettingsLog("alpha", 20)
	if len(log) < 5 || log[0].Actor != "dan" {
		t.Errorf("FAIL: the settings log has %d rows, latest by %q; want each change, by dan", len(log), log[0].Actor)
	}
	t.Log("ok: created, saved, listed by name only, deleted, and logged")
}