Ctrl K
ComunidadGuías…

coderd/boundary_logs_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package coderd_test

import (
	"context"
	"sync/atomic"
	"testing"

	"github.com/google/uuid"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/stretchr/testify/require"
	"google.golang.org/protobuf/types/known/timestamppb"

	agentproto "github.com/coder/coder/v2/agent/proto"
	"github.com/coder/coder/v2/coderd/coderdtest"
	"github.com/coder/coder/v2/coderd/database"
	"github.com/coder/coder/v2/coderd/database/dbauthz"
	"github.com/coder/coder/v2/coderd/database/dbfake"
	"github.com/coder/coder/v2/coderd/database/dbtestutil"
	"github.com/coder/coder/v2/coderd/database/dbtime"
	"github.com/coder/coder/v2/coderd/rbac"
	"github.com/coder/coder/v2/codersdk/agentsdk"
	"github.com/coder/coder/v2/testutil"
)

// TestReportBoundaryLogsAgentRBAC guards against regressions where
// a pre-insert read (e.g. GetBoundarySessionByID) would be silently denied for
// agents and prevent session creation.
func TestReportBoundaryLogsAgentRBAC(t *testing.T) {
	t.Parallel()

	store, ps := dbtestutil.NewDB(t)
	client := coderdtest.New(t, &coderdtest.Options{Database: store, Pubsub: ps})
	user := coderdtest.CreateFirstUser(t, client)
	r := dbfake.WorkspaceBuild(t, store, database.WorkspaceTable{
		OrganizationID: user.OrganizationID,
		OwnerID:        user.UserID,
	}).WithAgent().Do()

	ctx := testutil.Context(t, testutil.WaitLong)

	// Connect as a real workspace agent.
	ac := agentsdk.New(client.URL, agentsdk.WithFixedToken(r.AgentToken))
	conn, err := ac.ConnectRPC(ctx)
	require.NoError(t, err)
	defer conn.Close()

	agentClient := agentproto.NewDRPCAgentClient(conn)
	sessionID := uuid.New()

	_, err = agentClient.ReportBoundaryLogs(ctx, &agentproto.ReportBoundaryLogsRequest{
		SessionId:           sessionID.String(),
		ConfinedProcessName: "claude-code",
		Logs: []*agentproto.BoundaryLog{
			{
				Allowed:        true,
				Time:           timestamppb.New(dbtime.Now()),
				SequenceNumber: 0,
				Resource: &agentproto.BoundaryLog_HttpRequest_{
					HttpRequest: &agentproto.BoundaryLog_HttpRequest{
						Method:      "GET",
						Url:         "https://example.com",
						MatchedRule: "domain=example.com",
					},
				},
			},
		},
	})
	require.NoError(t, err)

	// Verify persistence via the raw store: because ReportBoundaryLogs swallows
	// DB errors and returns success regardless, only a direct read proves the
	// session and log were actually persisted under agent RBAC.
	sess, err := store.GetBoundarySessionByID(ctx, sessionID)
	require.NoError(t, err, "session must be persisted")
	require.Equal(t, r.Agents[0].ID, sess.WorkspaceAgentID)

	logs, err := store.ListBoundaryLogsBySessionID(ctx, database.ListBoundaryLogsBySessionIDParams{
		SessionID: sessionID,
	})
	require.NoError(t, err)
	require.Len(t, logs, 1, "log must be persisted")

	// Assert that the agent subject cannot read boundary sessions.
	memberRole, err := rbac.RoleByName(rbac.RoleMember())
	require.NoError(t, err)
	agentSubject := rbac.Subject{
		ID:    r.Workspace.OwnerID.String(),
		Roles: rbac.Roles{memberRole},
		Scope: rbac.WorkspaceAgentScope(rbac.WorkspaceAgentScopeParams{
			WorkspaceID: r.Workspace.ID,
			OwnerID:     r.Workspace.OwnerID,
			TemplateID:  r.Workspace.TemplateID,
			VersionID:   r.Build.TemplateVersionID,
		}),
	}.WithCachedASTValue()

	auth := rbac.NewStrictCachingAuthorizer(prometheus.NewRegistry())
	acsPtr := &atomic.Pointer[dbauthz.AccessControlStore]{}
	var acs dbauthz.AccessControlStore = dbauthz.AGPLTemplateAccessControlStore{}
	acsPtr.Store(&acs)
	authzStore := dbauthz.New(store, auth, testutil.Logger(t), acsPtr)

	agentCtx := dbauthz.As(context.Background(), agentSubject)
	_, err = authzStore.GetBoundarySessionByID(agentCtx, sessionID)
	require.True(t, dbauthz.IsNotAuthorizedError(err),
		"agents must not be able to read boundary sessions, got: %v", err)
}