package service import ( "encoding/json" "errors" "strings" "testing" "browser.local/platform/domain" ) func TestCoreServiceGeneratesRunDistributionWithEncryptedSingletonKey(t *testing.T) { svc, session, instance := newDistributionTestFixture(t) distribution, err := svc.GenerateRunDistributionForSession(session, domain.RunDistributionGenerateRequest{ ServerInstanceID: instance.ID, TargetOS: "linux", TargetArch: "amd64", IdempotencyKey: "idem-run-generate", }) if err != nil { t.Fatalf("generate run distribution: %v", err) } if distribution.KeyGeneration != 1 || distribution.SecretRef == "" || distribution.Status != domain.DistributionStatusAvailable { t.Fatalf("unexpected run distribution: %+v", distribution) } keys, err := svc.store.EncryptedComponentKeys().List(domain.EncryptedComponentKeyFilter{ ServerInstanceID: instance.ID, ComponentKind: domain.DistributionComponentRun, Status: domain.ComponentKeyStatusActive, }) if err != nil { t.Fatalf("list component keys: %v", err) } if len(keys) != 1 || keys[0].Generation != 1 || !strings.HasPrefix(keys[0].EncryptedKey, "enc:v1:") { t.Fatalf("expected one active encrypted run key, got %+v", keys) } config := readGeneratedPackageConfig(t, svc, session, distribution.ArtifactID) if config.AuthKey == "" || config.AuthKey == keys[0].EncryptedKey || strings.Contains(distribution.SecretRef, config.AuthKey) { t.Fatalf("run package key leaked through metadata or was not encrypted, config=%+v key=%+v distribution=%+v", config, keys[0], distribution) } auth, err := svc.AuthenticateComponent(domain.ComponentAuthenticationRequest{ ServerInstanceID: instance.ID, ComponentKind: domain.DistributionComponentRun, Generation: config.KeyGeneration, Key: config.AuthKey, }) if err != nil { t.Fatalf("authenticate run: %v", err) } if !auth.Allowed { t.Fatalf("expected current run key to authenticate, got %+v", auth) } second, err := svc.GenerateRunDistributionForSession(session, domain.RunDistributionGenerateRequest{ ServerInstanceID: instance.ID, TargetOS: "linux", TargetArch: "amd64", IdempotencyKey: "idem-run-generate-second", }) if err != nil { t.Fatalf("generate second run distribution: %v", err) } if second.KeyGeneration != 1 || second.SecretRef != distribution.SecretRef { t.Fatalf("expected second package to reuse current singleton key, got first=%+v second=%+v", distribution, second) } keys, err = svc.store.EncryptedComponentKeys().List(domain.EncryptedComponentKeyFilter{ ServerInstanceID: instance.ID, ComponentKind: domain.DistributionComponentRun, Status: domain.ComponentKeyStatusActive, }) if err != nil || len(keys) != 1 { t.Fatalf("expected one active key after second generation, keys=%+v err=%v", keys, err) } } func TestCoreServiceResetRunKeyRevokesOldPackagesAndRequiresRegeneration(t *testing.T) { svc, session, instance := newDistributionTestFixture(t) distribution, err := svc.GenerateRunDistributionForSession(session, domain.RunDistributionGenerateRequest{ ServerInstanceID: instance.ID, TargetOS: "linux", TargetArch: "amd64", IdempotencyKey: "idem-run-before-reset", }) if err != nil { t.Fatalf("generate run distribution: %v", err) } oldConfig := readGeneratedPackageConfig(t, svc, session, distribution.ArtifactID) reset, err := svc.ResetComponentKeyForSession(session, domain.ComponentKeyResetRequest{ ServerInstanceID: instance.ID, ComponentKind: domain.DistributionComponentRun, }) if err != nil { t.Fatalf("reset run key: %v", err) } if reset.Generation != 2 || reset.Status != domain.ComponentKeyStatusActive { t.Fatalf("expected reset key generation 2, got %+v", reset) } oldDistribution, err := svc.store.RunDistributions().Get(distribution.ID) if err != nil { t.Fatalf("get old distribution: %v", err) } if oldDistribution.Status != domain.DistributionStatusRevoked { t.Fatalf("expected old distribution revoked, got %+v", oldDistribution) } if _, err := svc.OpenArtifactDownloadForSession(session, domain.ArtifactDownloadReferenceRequest{ArtifactID: distribution.ArtifactID}); err == nil || !strings.Contains(err.Error(), "available") { t.Fatalf("expected old artifact download to be unavailable, got %v", err) } auth, err := svc.AuthenticateComponent(domain.ComponentAuthenticationRequest{ ServerInstanceID: instance.ID, ComponentKind: domain.DistributionComponentRun, Generation: oldConfig.KeyGeneration, Key: oldConfig.AuthKey, }) if err != nil { t.Fatalf("authenticate old key: %v", err) } if auth.Allowed || !strings.Contains(auth.Reason, "generation") { t.Fatalf("expected old package authentication denial, got %+v", auth) } newDistribution, err := svc.GenerateRunDistributionForSession(session, domain.RunDistributionGenerateRequest{ ServerInstanceID: instance.ID, TargetOS: "linux", TargetArch: "amd64", IdempotencyKey: "idem-run-after-reset", }) if err != nil { t.Fatalf("generate run distribution after reset: %v", err) } newConfig := readGeneratedPackageConfig(t, svc, session, newDistribution.ArtifactID) if newDistribution.KeyGeneration != 2 || newConfig.AuthKey == oldConfig.AuthKey { t.Fatalf("expected regenerated package with new generation/key, old=%+v new=%+v", oldConfig, newConfig) } auth, err = svc.AuthenticateComponent(domain.ComponentAuthenticationRequest{ ServerInstanceID: instance.ID, ComponentKind: domain.DistributionComponentRun, Generation: newConfig.KeyGeneration, Key: newConfig.AuthKey, }) if err != nil { t.Fatalf("authenticate new key: %v", err) } if !auth.Allowed { t.Fatalf("expected regenerated package to authenticate, got %+v", auth) } } func TestCoreServiceBuildsClientManagerWithDistinctKeyAndAuditsSensitiveOperations(t *testing.T) { svc, session, instance := newDistributionTestFixture(t) runDistribution, err := svc.GenerateRunDistributionForSession(session, domain.RunDistributionGenerateRequest{ ServerInstanceID: instance.ID, TargetOS: "linux", TargetArch: "amd64", IdempotencyKey: "idem-run-for-client", }) if err != nil { t.Fatalf("generate run distribution: %v", err) } if _, err := svc.OpenArtifactDownloadForSession(session, domain.ArtifactDownloadReferenceRequest{ArtifactID: runDistribution.ArtifactID}); err != nil { t.Fatalf("open run download: %v", err) } runConfig := readGeneratedPackageConfig(t, svc, session, runDistribution.ArtifactID) clientDistribution, err := svc.GenerateClientManagerDistributionForSession(session, domain.ClientManagerBuildRequest{ ServerInstanceID: instance.ID, ProfileKey: "scum-client-manager", TargetOS: "windows", TargetArch: "amd64", RepositoryURL: "https://github.com/F88888/scum_client.git", SourceRevision: "main", IdempotencyKey: "idem-client-manager", }) if err != nil { t.Fatalf("generate client-manager distribution: %v", err) } clientConfig := readGeneratedPackageConfig(t, svc, session, clientDistribution.ArtifactID) if clientDistribution.KeyGeneration != 1 || clientDistribution.BuildJobID == "" || clientDistribution.Status != domain.DistributionStatusAvailable { t.Fatalf("unexpected client-manager distribution: %+v", clientDistribution) } if clientConfig.AuthKey == runConfig.AuthKey || clientDistribution.SecretRef == runDistribution.SecretRef { t.Fatalf("client-manager must use a distinct key/ref, run=%+v client=%+v", runConfig, clientConfig) } build, err := svc.store.ClientManagerBuildJobs().Get(clientDistribution.BuildJobID) if err != nil { t.Fatalf("get build job: %v", err) } if build.Status != domain.DistributionJobStatusSucceeded || build.RepositoryURL != "https://github.com/F88888/scum_client.git" || build.SourceRevision != "main" { t.Fatalf("unexpected build job: %+v", build) } if build.LogsRef == "" || !strings.HasPrefix(build.LogsRef, "artifact://") { t.Fatalf("expected redacted build log artifact ref, got %+v", build) } packagePayload := readClientManagerPackage(t, svc, session, clientDistribution.ArtifactID) if packagePayload.Checkout.CheckoutRef != "branch/main" || packagePayload.Config.AuthKey != clientConfig.AuthKey || packagePayload.KeyFingerprint == "" { t.Fatalf("expected package checkout metadata and injected config, got %+v", packagePayload) } if len(packagePayload.OutputArtifacts) == 0 || packagePayload.BuildLogRef != build.LogsRef { t.Fatalf("expected output artifacts and build log ref, got %+v build=%+v", packagePayload, build) } buildLog := readArtifactString(t, svc, session, strings.TrimPrefix(build.LogsRef, "artifact://")) for _, expected := range []string{"client-manager checkout prepared", "checkoutRef=branch/main", "dependencyCheck=typed build profile accepted", "configInjection=secret ref"} { if !strings.Contains(buildLog, expected) { t.Fatalf("expected build log to contain %q, got %q", expected, buildLog) } } for _, forbidden := range []string{runConfig.AuthKey, clientConfig.AuthKey, "password=", "unix://", "tcp://", "/Users/", "mysql://", "sqlite://"} { if strings.Contains(buildLog, forbidden) { t.Fatalf("build log leaked forbidden fragment %q: %s", forbidden, buildLog) } } _, err = svc.GenerateClientManagerDistributionForSession(session, domain.ClientManagerBuildRequest{ ServerInstanceID: instance.ID, ProfileKey: "scum-client-manager", TargetOS: "darwin", TargetArch: "amd64", RepositoryURL: "https://github.com/F88888/scum_client.git", IdempotencyKey: "idem-client-manager-denied", }) if err == nil || !strings.Contains(err.Error(), "targetOs") { t.Fatalf("expected unsupported target denial, got %v", err) } if _, err := svc.ResetComponentKeyForSession(session, domain.ComponentKeyResetRequest{ ServerInstanceID: instance.ID, ComponentKind: domain.DistributionComponentClientManager, ComponentKey: "scum-client-manager", }); err != nil { t.Fatalf("reset client-manager key: %v", err) } auth, err := svc.AuthenticateComponent(domain.ComponentAuthenticationRequest{ ServerInstanceID: instance.ID, ComponentKind: domain.DistributionComponentClientManager, ComponentKey: "scum-client-manager", Generation: clientConfig.KeyGeneration, Key: clientConfig.AuthKey, }) if err != nil { t.Fatalf("authenticate old client key: %v", err) } if auth.Allowed { t.Fatalf("expected old client-manager key to be denied after reset, got %+v", auth) } audits, err := svc.ListAuditEvents(domain.AuditEventFilter{ResourceID: instance.ID}) if err != nil { t.Fatalf("list audits: %v", err) } actions := map[string]bool{} for _, audit := range audits { actions[audit.Action] = true for _, forbidden := range []string{runConfig.AuthKey, clientConfig.AuthKey, "password=", "unix://", "/Users/"} { if strings.Contains(audit.Summary, forbidden) { t.Fatalf("audit leaked forbidden fragment %q in %+v", forbidden, audit) } } } for _, action := range []string{"run.generate", "run.download", "client-manager.build", "client-manager.build.denied", "runtime-key.reset"} { if !actions[action] { t.Fatalf("expected audit action %q in %+v", action, audits) } } } func newDistributionTestFixture(t *testing.T) (*CoreService, string, domain.ServerInstance) { t.Helper() svc := newTestCoreService() plugin, endpoint := createPluginAndRunEndpoint(t, svc) plugin.SupportedOS = []string{"linux", "windows"} plugin.DeclaredPermissions = append(plugin.DeclaredPermissions, "server.run.distribution", "server.client-manager.manage", "server.dependencies.manage", ) plugin.RequiredRunCapabilities = append(plugin.RequiredRunCapabilities, domain.JobCapabilityRunSelfUpdate, domain.JobCapabilityDependenciesCheck, domain.JobCapabilityDependenciesInstall, domain.JobCapabilityLogsBackfill, ) plugin.BridgeActions = append(plugin.BridgeActions, string(domain.PluginBridgeActionRunDistribution), string(domain.PluginBridgeActionClientManager), string(domain.PluginBridgeActionDependenciesRequest), string(domain.PluginBridgeActionLogsBackfillRequest), ) if err := svc.store.GamePlugins().Update(plugin); err != nil { t.Fatalf("update plugin fixture: %v", err) } endpoint.Capabilities = append(endpoint.Capabilities, domain.JobCapabilityRunSelfUpdate, domain.JobCapabilityDependenciesCheck, domain.JobCapabilityDependenciesInstall, domain.JobCapabilityLogsBackfill, ) if err := svc.store.RunEndpoints().Update(endpoint); err != nil { t.Fatalf("update endpoint fixture: %v", err) } session := createServiceUserAndLogin(t, svc, domain.User{ ID: "user-distribution-owner", DisplayName: "Distribution Owner", Email: "distribution-owner@example.test", Roles: []string{"server-owner"}, PasswordHash: "secret-password", }) instance, err := svc.CreateServerInstanceForSession(session, domain.ServerInstance{ ID: "server-distribution", PluginID: plugin.ID, RunEndpointID: endpoint.ID, Name: "Distribution Server", State: domain.ServerInstanceStateReady, }) if err != nil { t.Fatalf("create distribution server: %v", err) } return svc, session, instance } func readGeneratedPackageConfig(t *testing.T, svc *CoreService, session string, artifactID string) generatedPackageConfig { t.Helper() content, err := svc.ReadArtifactContentForSession(session, domain.ArtifactContentRequest{ArtifactID: artifactID}) if err != nil { t.Fatalf("read artifact content: %v", err) } var config generatedPackageConfig if err := json.Unmarshal(content.Payload, &config); err != nil { t.Fatalf("unmarshal generated config: %v", err) } if config.AuthKey == "" { var packagePayload generatedClientManagerPackage if err := json.Unmarshal(content.Payload, &packagePayload); err != nil { t.Fatalf("unmarshal generated client-manager package: %v", err) } config = packagePayload.Config } if config.AuthKey == "" || config.SecretRef == "" || config.KeyGeneration <= 0 { t.Fatalf("generated package config is incomplete: %+v", config) } return config } func readClientManagerPackage(t *testing.T, svc *CoreService, session string, artifactID string) generatedClientManagerPackage { t.Helper() content, err := svc.ReadArtifactContentForSession(session, domain.ArtifactContentRequest{ArtifactID: artifactID}) if err != nil { t.Fatalf("read client-manager package content: %v", err) } var packagePayload generatedClientManagerPackage if err := json.Unmarshal(content.Payload, &packagePayload); err != nil { t.Fatalf("unmarshal generated client-manager package: %v", err) } return packagePayload } func readArtifactString(t *testing.T, svc *CoreService, session string, artifactID string) string { t.Helper() content, err := svc.ReadArtifactContentForSession(session, domain.ArtifactContentRequest{ArtifactID: artifactID}) if err != nil { t.Fatalf("read artifact content: %v", err) } return string(content.Payload) } func TestCoreServiceDeniesRunDistributionWithoutPluginDeclaration(t *testing.T) { svc := newTestCoreService() plugin, endpoint := createPluginAndRunEndpoint(t, svc) session := createServiceUserAndLogin(t, svc, domain.User{ ID: "user-distribution-denied", DisplayName: "Distribution Denied", Email: "distribution-denied@example.test", Roles: []string{"server-owner"}, PasswordHash: "secret-password", }) instance, err := svc.CreateServerInstanceForSession(session, domain.ServerInstance{ ID: "server-distribution-denied", PluginID: plugin.ID, RunEndpointID: endpoint.ID, Name: "Distribution Denied Server", State: domain.ServerInstanceStateReady, }) if err != nil { t.Fatalf("create denied server: %v", err) } _, err = svc.GenerateRunDistributionForSession(session, domain.RunDistributionGenerateRequest{ ServerInstanceID: instance.ID, TargetOS: "linux", TargetArch: "amd64", IdempotencyKey: "idem-run-denied", }) if !errors.Is(err, ErrForbidden) { t.Fatalf("expected plugin declaration denial, got %v", err) } }