// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later package app import ( "context" "encoding/json" "errors" "os" "testing" "time" "git.stormux.org/storm/cthulhu/internal/interaction" "git.stormux.org/storm/cthulhu/internal/tentacle" "git.stormux.org/storm/cthulhu/internal/tentaclewire" ) type consentFormRequest struct { ctx context.Context form interaction.Form reply chan internalUIResult } type consentTestForms struct{ requests chan consentFormRequest } func (forms *consentTestForms) Open(context.Context, interaction.Form) (<-chan internalUIResult, error) { return nil, errors.New("ordinary form used for consent") } func (forms *consentTestForms) Notice(context.Context, tentaclewire.Presentation) {} func (forms *consentTestForms) OpenLocal(ctx context.Context, form interaction.Form) (<-chan internalUIResult, error) { reply := make(chan internalUIResult, 1) forms.requests <- consentFormRequest{ctx, form, reply} output := make(chan internalUIResult, 1) go func() { select { case value := <-reply: output <- value case <-ctx.Done(): output <- internalUIResult{Cancelled: true} } }() return output, nil } func consent_runtime_fixture(t *testing.T) (*tentacleRuntime, *tentacle.Session, *consentTestForms, *focusGate) { t.Helper() executable, err := os.Executable() if err != nil { t.Fatal(err) } manager, err := tentacle.NewManager(tentacle.DefaultLimits(), nil) if err != nil { t.Fatal(err) } t.Cleanup(func() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := manager.Shutdown(ctx); err != nil { t.Error(err) } }) policy := tentacle.NewRemotePolicy() if err := manager.UseRemotePolicy(policy); err != nil { t.Fatal(err) } session, err := manager.Start(t.Context(), tentacle.Spec{ID: "consent-probe", Program: []string{executable, "-test.run=^TestRemoteManagementPeer$", "--", "remote-management-peer"}, SessionEvents: true, RemoteGrants: []string{"output-observe", "clipboard-exchange"}}) if err != nil { t.Fatal(err) } select { case <-session.Ready(): case <-time.After(5 * time.Second): t.Fatal("peer not ready") } if err := session.Activate(); err != nil { t.Fatal(err) } forms := &consentTestForms{requests: make(chan consentFormRequest, 1)} runtime := &tentacleRuntime{remotePolicy: policy, forms: forms, sessions: []*tentacle.Session{session}, interactionSlots: make(chan struct{}, 1)} return runtime, session, forms, &focusGate{remotePolicy: policy} } func TestRemoteConsentFormDefaultsAndRevocation(t *testing.T) { for _, action := range []string{"approve", "cancel", "empty", "lock", "handoff", "revoke", "disable"} { t.Run(action, func(t *testing.T) { runtime, session, forms, gate := consent_runtime_fixture(t) ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second) defer cancel() result := make(chan error, 1) go func() { _, err := runtime.authorize_remote(ctx, "consent-probe", "controlled", []string{"output-observe", "clipboard-exchange"}) result <- err }() var request consentFormRequest select { case request = <-forms.requests: case <-ctx.Done(): t.Fatal("no consent form") } if _, err := interaction.NewSession(request.form); err != nil { t.Fatal("invalid consent form", err) } if request.form.Elements[0].ID != "cancel" { t.Fatal("approval was default action") } for _, element := range request.form.Elements { if element.Checked { t.Fatal("permission preselected") } } reply := internalUIResult{Activated: "approve", Values: map[string]interaction.Value{"output-observe": {Checked: true}}} switch action { case "cancel": reply.Activated = "cancel" case "empty": reply.Values = nil case "lock": gate.SetLocked(true, nil, nil, nil, nil, &brailleWorker{}) gate.SetLocked(false, nil, nil, nil, nil, &brailleWorker{}) case "handoff": gate.SetSuspended(true) gate.SetSuspended(false) case "revoke": runtime.revokeManagedRemote("consent-probe") case "disable": session.Disable() } request.reply <- reply select { case err := <-result: if action == "approve" && err != nil { t.Fatal(err) } if action != "approve" && err == nil { t.Fatal("stale/denied consent issued action") } case <-ctx.Done(): t.Fatal("consent worker did not finish") } if action != "approve" { return } pending, err := session.Invoke(ctx, "open", nil) if err != nil { t.Fatal(err) } var lease tentaclewire.RemoteLease select { case opened := <-pending: if opened.Err != nil || json.Unmarshal(opened.Value, &lease) != nil { t.Fatal("approved action failed", opened.Err) } case <-ctx.Done(): t.Fatal("open timed out") } delivery, err := session.RemoteContext(lease, "output-observe") if err != nil { t.Fatal(err) } if _, err := session.RemoteContext(lease, "clipboard-exchange"); err == nil { t.Fatal("unchecked permission granted") } gate.SetSuspended(true) if delivery.Err() == nil { t.Fatal("handoff did not synchronously cancel delivery") } gate.SetSuspended(false) if _, err := session.RemoteContext(lease, "output-observe"); err == nil { t.Fatal("handoff resume revived access") } }) } } func TestRemoteGrantedTentacleUsesLocalForms(t *testing.T) { runtime, session, forms, _ := consent_runtime_fixture(t) form := interaction.Form{Title: "Certificate review", Elements: []interaction.Element{{ID: "cancel", Label: "Cancel", Kind: interaction.Button}}} if _, err := runtime.open_tentacle_form(t.Context(), tentacleCommand{session: session}, form); err != nil { t.Fatal(err) } select { case <-forms.requests: default: t.Fatal("remote form used ordinary input") } } type ordinaryOnlyForms struct{ tentacleForms } func TestRemoteConsentCannotFallBackToOrdinaryForms(t *testing.T) { runtime, session, _, _ := consent_runtime_fixture(t) runtime.forms = ordinaryOnlyForms{runtime.forms} if _, err := runtime.authorize_remote(t.Context(), "consent-probe", "controlled", []string{"output-observe"}); err == nil { t.Fatal("consent fell back to ordinary input") } if _, err := runtime.open_tentacle_form(t.Context(), tentacleCommand{session: session}, remote_consent_form("consent-probe", "controlled", []string{"output-observe"})); err == nil { t.Fatal("sensitive form fell back to ordinary input") } } func TestRemotePolicyAttachedToRuntimeManagers(t *testing.T) { runtime := &tentacleRuntime{remotePolicy: tentacle.NewRemotePolicy(), newManager: func() (*tentacle.Manager, error) { return tentacle.NewManager(tentacle.DefaultLimits(), nil) }} manager, err := runtime.makeManager() if err != nil { t.Fatal(err) } if err := manager.UseRemotePolicy(tentacle.NewRemotePolicy()); err == nil { t.Fatal("reader manager omitted policy") } } func TestRemoteOptionalGrantsRespectInstallationAndLocalSelection(t *testing.T) { runtime, session, forms, _ := consent_runtime_fixture(t) type result struct { action tentaclewire.SessionEvent err error } completed := make(chan result, 1) go func() { action, err := runtime.authorize_remote_request(t.Context(), session, tentaclewire.RemoteRequest{Role: "controlled", Grants: []string{"output-observe"}, OptionalGrants: []string{"clipboard-exchange", "keyboard-inject", "braille-stream"}}) completed <- result{action, err} }() request := next_remote_form(t, forms) offered := map[string]bool{} for _, element := range request.form.Elements { if element.Kind == interaction.CheckBox { offered[element.ID] = true if element.Checked { t.Fatal("grant preselected") } } } if !offered["output-observe"] || !offered["clipboard-exchange"] || offered["keyboard-inject"] || offered["braille-stream"] { t.Fatal("uninstalled access offered", offered) } request.reply <- internalUIResult{Activated: "approve", Values: map[string]interaction.Value{"output-observe": {Checked: true}, "keyboard-inject": {Checked: true}}} select { case value := <-completed: if value.err != nil || len(value.action.Grants) != 1 || value.action.Grants[0] != "output-observe" { t.Fatal("unselected or uninstalled grant authorized", value) } case <-time.After(time.Second): t.Fatal("approval did not finish") } }