Files

95 lines
4.3 KiB
Go

package web
import (
"bytes"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"gitea.dooplex.hu/admin/felhom-controller/internal/stacks"
)
// R-709 (v0.280.0) — an installed app's `type: password` value is never in its settings page's HTML; it is
// fetched on demand, like a `type: secret` (R-254). The pre-deploy form keeps its generator.
const testAdminPassword = "TESTONLY-admin-pw-Qz72"
func renderDeployWithPassword(t *testing.T, alreadyDeployed bool, restored bool) string {
t.Helper()
s := securityHarness(t)
s.loadTemplates()
data := map[string]interface{}{
"Page": "stacks", "Title": "Telepítés", "Domain": "example.hu",
"Stack": stacks.Stack{Name: "grafana", Deployed: alreadyDeployed},
"Meta": stacks.Metadata{DisplayName: "Grafana", Slug: "grafana"},
"AlreadyDeployed": alreadyDeployed,
"UserFields": []stacks.DeployField{
{EnvVar: "ADMIN_PASSWORD", Label: "Admin jelszó", Type: "password", Generate: "password:24:special"},
},
// The PRE-FIX handler shape: the stored value in DeployedFieldValues. The template must not print it
// even then (the handler now also drops it — TestR709_TheRevealEndpointServesAnInstalledPassword).
"DeployedFieldValues": map[string]string{"ADMIN_PASSWORD": testAdminPassword},
"RestoredLogins": map[string]bool{"ADMIN_PASSWORD": restored},
}
var buf bytes.Buffer
if err := s.tmpl.ExecuteTemplate(&buf, "deploy", data); err != nil {
t.Fatalf("render deploy: %v", err)
}
return buf.String()
}
// COMPANION RED-PROOF: put `value="{{index $.DeployedFieldValues .EnvVar}}"` back on the deployed password input
// → the first assertion fails with the password in the body.
func TestR709_AnInstalledAppsPasswordIsNotInThePage(t *testing.T) {
html := renderDeployWithPassword(t, true, false)
if strings.Contains(html, testAdminPassword) {
t.Fatal("R-709: the installed app's admin password is in the HTML of its settings page")
}
if !strings.Contains(html, `onclick="revealPasswordField('grafana', 'ADMIN_PASSWORD', this)"`) {
t.Fatal("no reveal control: the household cannot see its own password")
}
// A login a restore generated is not the app's login (R-694): no reveal for it.
if html := renderDeployWithPassword(t, true, true); strings.Contains(html, `revealPasswordField('grafana'`) || strings.Contains(html, testAdminPassword) {
t.Fatal("a restore-generated login is offered for reveal")
}
// The pre-deploy form keeps its generator, with the field's own spec.
if html := renderDeployWithPassword(t, false, false); !strings.Contains(html, `generatePassword('field-ADMIN_PASSWORD', 'field-confirm-ADMIN_PASSWORD', 'password:24:special')`) {
t.Fatal("the install form lost its generator (or its spec)")
}
}
// The reveal endpoint serves an installed app's password field, refuses a restore-generated one and an unknown
// field. COMPANION RED-PROOF: drop the new password branch in appAutoFieldRevealHandler → the first case 403s.
func TestR709_TheRevealEndpointServesAnInstalledPassword(t *testing.T) {
s := gateHarness(t)
app := filepath.Join(s.cfg.Paths.StacksDir, "gapp")
if err := os.WriteFile(filepath.Join(app, ".felhom.yml"), []byte("display_name: G\nslug: gapp\ndeploy_fields:\n - env_var: ADMIN_PASSWORD\n type: password\n - env_var: OLD_PW\n type: password\n"), 0o644); err != nil {
t.Fatal(err)
}
if err := stacks.SaveAppConfig(app, &stacks.AppConfig{Deployed: true, Env: map[string]string{"ADMIN_PASSWORD": testAdminPassword, "OLD_PW": "x"}, RestoredLogins: []string{"OLD_PW"}}, nil, nil); err != nil {
t.Fatal(err)
}
if err := s.stackMgr.ScanStacks(); err != nil {
t.Fatal(err)
}
ask := func(field string) *httptest.ResponseRecorder {
r := httptest.NewRequest(http.MethodPost, "/stacks/gapp/auto-field/reveal", strings.NewReader("env_var="+field))
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
w := httptest.NewRecorder()
s.appAutoFieldRevealHandler(w, r, "gapp")
return w
}
if w := ask("ADMIN_PASSWORD"); w.Code != http.StatusOK || !strings.Contains(w.Body.String(), testAdminPassword) {
t.Fatalf("installed password: %d %s", w.Code, w.Body.String())
}
if w := ask("OLD_PW"); w.Code != http.StatusForbidden {
t.Fatalf("a restore-generated login was revealed: %d", w.Code)
}
if w := ask("NOPE"); w.Code != http.StatusForbidden {
t.Fatalf("an unknown field: %d", w.Code)
}
}