Include all accounts in access-review fetch

Drivers whose source API surfaces an account-status signal return every
account (including inactive / suspended / deleted) and populate Active
accordingly; drivers without such a signal no longer fabricate it.

The fetch pipeline records every account with decision PENDING and no
flags. Producing flags or a non-PENDING decision is reserved to human
reviewers or a purpose-built agent run against the campaign -- the engine
must not form an implicit verdict.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
This commit is contained in:
Aurélien Sibiril
2026-04-17 10:46:12 +02:00
parent 9510d91c61
commit da80ce3eb1
21 changed files with 30 additions and 23 deletions

View File

@@ -68,7 +68,7 @@ func (d *BrexDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error)
Email: u.Email,
FullName: u.FirstName + " " + u.LastName,
Role: u.Role,
Active: u.Status == "ACTIVE",
Active: new(u.Status == "ACTIVE"),
IsAdmin: false,
ExternalID: u.ID,
MFAStatus: coredata.MFAStatusUnknown,

View File

@@ -184,7 +184,7 @@ func (d *CloudflareDriver) queryAllMembers(ctx context.Context, accountID string
Email: m.User.Email,
FullName: m.User.FirstName + " " + m.User.LastName,
Role: role,
Active: m.Status == "accepted",
Active: new(m.Status == "accepted"),
IsAdmin: isAdmin,
ExternalID: m.ID,
MFAStatus: mfaStatus,

View File

@@ -88,7 +88,7 @@ func (d *CSVDriver) ListAccounts(_ context.Context) ([]AccountRecord, error) {
record.IsAdmin = strings.TrimSpace(strings.ToLower(row[idx])) == "true"
}
if idx, ok := colIndex["active"]; ok && idx < len(row) {
record.Active = strings.TrimSpace(strings.ToLower(row[idx])) == "true"
record.Active = new(strings.TrimSpace(strings.ToLower(row[idx])) == "true")
}
if idx, ok := colIndex["external_id"]; ok && idx < len(row) {
record.ExternalID = strings.TrimSpace(row[idx])

View File

@@ -93,7 +93,7 @@ func (d *DocuSignDriver) ListAccounts(ctx context.Context) ([]AccountRecord, err
FullName: u.UserName,
Role: u.PermissionProfileName,
JobTitle: u.JobTitle,
Active: strings.EqualFold(u.UserStatus, "active"),
Active: new(strings.EqualFold(u.UserStatus, "active")),
IsAdmin: strings.EqualFold(u.IsAdmin, "True"),
ExternalID: u.UserID,
MFAStatus: coredata.MFAStatusUnknown,

View File

@@ -23,13 +23,23 @@ import (
)
// AccountRecord represents a single account from an access source or identity
// source. All fields are best-effort; sources populate what they can.
// source. All fields are best-effort; sources populate what they can. Drivers
// must return ALL accounts the source exposes (including inactive / suspended
// / deleted); classification is the job of the reviewer or of an agent run
// against the campaign, not of the fetch pipeline.
//
// Active is three-valued: nil means the source API has no explicit
// account-status signal for this account (the driver cannot tell), a non-nil
// pointer means the driver observed an explicit signal (true = active at
// source, false = deactivated / suspended / deleted). Drivers whose API does
// not distinguish active from deactivated accounts must leave Active nil
// rather than fabricate a value.
type AccountRecord struct {
Email string
FullName string
Role string // system role/permission (e.g. "Admin", "Viewer")
JobTitle string // HR job title / department (e.g. "Software Engineer")
Active bool
Active *bool
IsAdmin bool
MFAStatus coredata.MFAStatus
AuthMethod coredata.AccessEntryAuthMethod

View File

@@ -119,7 +119,7 @@ func (d *GitHubDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
Email: profile.Email,
FullName: fullName,
Role: membership.Role,
Active: membership.State == "active",
Active: new(membership.State == "active"),
IsAdmin: membership.Role == "admin",
MFAStatus: mfaStatus,
AuthMethod: coredata.AccessEntryAuthMethodUnknown,

View File

@@ -109,7 +109,7 @@ func (d *GoogleWorkspaceDriver) ListAccounts(ctx context.Context) ([]AccountReco
rec := AccountRecord{
Email: u.PrimaryEmail,
FullName: u.Name.FullName,
Active: !u.Suspended && !u.Archived,
Active: new(!u.Suspended && !u.Archived),
IsAdmin: u.IsAdmin,
ExternalID: u.Id,
MFAStatus: coredata.MFAStatusUnknown,

View File

@@ -99,7 +99,6 @@ func (d *HubSpotDriver) ListAccounts(ctx context.Context) ([]AccountRecord, erro
Email: u.Email,
FullName: fullName,
Role: role,
Active: true,
IsAdmin: u.SuperAdmin,
ExternalID: u.ID,
MFAStatus: coredata.MFAStatusUnknown,

View File

@@ -67,7 +67,6 @@ func (d *IntercomDriver) ListAccounts(ctx context.Context) ([]AccountRecord, err
FullName: a.Name,
Role: intercomRole(a.HasInboxSeat),
JobTitle: a.JobTitle,
Active: true,
IsAdmin: false, // Intercom API does not expose admin role information
ExternalID: a.ID,
MFAStatus: coredata.MFAStatusUnknown,

View File

@@ -97,7 +97,7 @@ func (d *LinearDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
Email: u.Email,
FullName: u.Name,
Role: linearRole(u.Admin, u.Guest),
Active: u.Active,
Active: new(u.Active),
IsAdmin: u.Admin,
ExternalID: u.ID,
MFAStatus: coredata.MFAStatusUnknown,

View File

@@ -81,7 +81,6 @@ func (d *NotionDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
Email: email,
FullName: u.Name,
Role: "Member",
Active: true,
IsAdmin: false,
ExternalID: u.ID,
MFAStatus: coredata.MFAStatusUnknown,

View File

@@ -93,7 +93,7 @@ func (d *OnePasswordDriver) ListAccounts(ctx context.Context) ([]AccountRecord,
record := AccountRecord{
Email: email,
FullName: u.DisplayName,
Active: u.Active,
Active: new(u.Active),
ExternalID: u.ID,
MFAStatus: coredata.MFAStatusUnknown,
AuthMethod: coredata.AccessEntryAuthMethodUnknown,

View File

@@ -86,7 +86,7 @@ func (d *OnePasswordUsersAPIDriver) ListAccounts(ctx context.Context) ([]Account
record := AccountRecord{
Email: u.Email,
FullName: u.DisplayName,
Active: u.State == "ACTIVE",
Active: new(u.State == "ACTIVE"),
ExternalID: u.ID,
MFAStatus: coredata.MFAStatusUnknown,
AuthMethod: coredata.AccessEntryAuthMethodUnknown,

View File

@@ -68,7 +68,7 @@ func (d *OpenAIDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
Email: u.Email,
FullName: u.Name,
Role: openaiRole(u.Role),
Active: !u.Disabled,
Active: new(!u.Disabled),
IsAdmin: u.Role == "owner",
ExternalID: u.ID,
MFAStatus: coredata.MFAStatusUnknown,

View File

@@ -69,7 +69,7 @@ func (d *ProboMembershipsDriver) ListAccounts(ctx context.Context) ([]AccountRec
Email: account.Email,
FullName: account.FullName,
Role: role,
Active: account.State == string(coredata.ProfileStateActive),
Active: new(account.State == string(coredata.ProfileStateActive)),
IsAdmin: isAdmin,
ExternalID: account.ID.String(),
CreatedAt: &createdAt,

View File

@@ -57,7 +57,6 @@ func (d *ResendDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
for _, k := range resp.Data {
record := AccountRecord{
FullName: k.Name,
Active: true,
IsAdmin: false,
ExternalID: k.ID,
MFAStatus: coredata.MFAStatusUnknown,

View File

@@ -144,7 +144,7 @@ func (d *SentryDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
Email: m.Email,
FullName: fullName,
Role: m.OrgRole,
Active: active,
Active: new(active),
IsAdmin: isAdmin,
ExternalID: m.ID,
MFAStatus: mfaStatus,

View File

@@ -101,7 +101,7 @@ func (d *SlackDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error)
FullName: m.RealName,
JobTitle: m.Profile.Title,
Role: slackRole(m),
Active: !m.Deleted,
Active: new(!m.Deleted),
IsAdmin: m.IsAdmin || m.IsOwner || m.IsPrimaryOwner,
ExternalID: m.ID,
MFAStatus: slackMFAStatus(m.Has2FA),

View File

@@ -65,7 +65,6 @@ func (d *SupabaseDriver) ListAccounts(ctx context.Context) ([]AccountRecord, err
Email: m.Email,
FullName: m.UserName,
Role: m.RoleName,
Active: true,
IsAdmin: isAdmin,
ExternalID: m.UserID,
MFAStatus: mfaStatus,

View File

@@ -115,7 +115,7 @@ func (d *TallyDriver) listUsers(ctx context.Context) ([]AccountRecord, error) {
record := AccountRecord{
Email: u.Email,
FullName: u.FullName,
Active: !u.IsDeleted,
Active: new(!u.IsDeleted),
ExternalID: u.ID,
MFAStatus: mfaStatus,
AuthMethod: coredata.AccessEntryAuthMethodUnknown,
@@ -169,7 +169,7 @@ func (d *TallyDriver) listInvites(ctx context.Context) ([]AccountRecord, error)
for _, inv := range invites {
record := AccountRecord{
Email: inv.Email,
Active: false,
Active: new(false),
ExternalID: inv.ID,
MFAStatus: coredata.MFAStatusUnknown,
AuthMethod: coredata.AccessEntryAuthMethodUnknown,

View File

@@ -14,7 +14,9 @@
package accessreview
import "testing"
import (
"testing"
)
func TestNormalizeAccountKey(t *testing.T) {
t.Parallel()