Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 208 additions & 0 deletions pkg/anonymize/annotations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package anonymize

import (
v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
)

// anonymizeResourceTypeAnnotations processes all annotations on a ResourceType,
// anonymizing known types and dropping unknown types.
func (a *Anonymizer) anonymizeResourceTypeAnnotations(rt *v2.ResourceType) error {
if rt == nil {
return nil
}

var result []*anypb.Any
for _, ann := range rt.GetAnnotations() {
if processed, err := a.processResourceTypeAnnotation(ann); err != nil {
return err
} else if processed != nil {
result = append(result, processed)
}
// Unknown types are dropped
}

rt.SetAnnotations(result)
return nil
}

// processResourceTypeAnnotation processes a single annotation from a ResourceType.
// Returns nil if the annotation should be dropped.
func (a *Anonymizer) processResourceTypeAnnotation(ann *anypb.Any) (*anypb.Any, error) {
// ChildResourceType - anonymize resource type ID
childRT := &v2.ChildResourceType{}
if ann.MessageIs(childRT) {
if err := ann.UnmarshalTo(childRT); err != nil {
return nil, err
}
if childRT.GetResourceTypeId() != "" {
childRT.SetResourceTypeId(a.hasher.AnonymizeResourceType(childRT.GetResourceTypeId()))
}
return anypb.New(childRT)
}

// ExternalLink - anonymize URL
externalLink := &v2.ExternalLink{}
if ann.MessageIs(externalLink) {
if err := ann.UnmarshalTo(externalLink); err != nil {
return nil, err
}
if externalLink.GetUrl() != "" {
externalLink.SetUrl(a.hasher.AnonymizeURL(externalLink.GetUrl()))
}
return anypb.New(externalLink)
}

// Empty marker types - preserve as-is (no PII)
for _, marker := range []proto.Message{
&v2.SkipEntitlementsAndGrants{},
&v2.SkipGrants{},
&v2.SkipEntitlements{},
} {
if ann.MessageIs(marker) {
return ann, nil
}
}

// Unknown type - drop it
return nil, nil
}
Comment on lines +32 to +70
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle errors from anypb.New().

The anypb.New() function returns (*anypb.Any, error), but the error is not checked at lines 42 and 54. Marshaling failures could be silently ignored, leading to data loss or incorrect anonymization.

Apply this diff to handle errors properly:

 		if childRT.GetResourceTypeId() != "" {
 			childRT.SetResourceTypeId(a.hasher.AnonymizeResourceType(childRT.GetResourceTypeId()))
 		}
-		return anypb.New(childRT)
+		anyMsg, err := anypb.New(childRT)
+		if err != nil {
+			return nil, err
+		}
+		return anyMsg, nil
 	}
 
 	// ExternalLink - anonymize URL
 	externalLink := &v2.ExternalLink{}
 	if ann.MessageIs(externalLink) {
 		if err := ann.UnmarshalTo(externalLink); err != nil {
 			return nil, err
 		}
 		if externalLink.GetUrl() != "" {
 			externalLink.SetUrl(a.hasher.AnonymizeURL(externalLink.GetUrl()))
 		}
-		return anypb.New(externalLink)
+		anyMsg, err := anypb.New(externalLink)
+		if err != nil {
+			return nil, err
+		}
+		return anyMsg, nil
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (a *Anonymizer) processResourceTypeAnnotation(ann *anypb.Any) (*anypb.Any, error) {
// ChildResourceType - anonymize resource type ID
childRT := &v2.ChildResourceType{}
if ann.MessageIs(childRT) {
if err := ann.UnmarshalTo(childRT); err != nil {
return nil, err
}
if childRT.GetResourceTypeId() != "" {
childRT.SetResourceTypeId(a.hasher.AnonymizeResourceType(childRT.GetResourceTypeId()))
}
return anypb.New(childRT)
}
// ExternalLink - anonymize URL
externalLink := &v2.ExternalLink{}
if ann.MessageIs(externalLink) {
if err := ann.UnmarshalTo(externalLink); err != nil {
return nil, err
}
if externalLink.GetUrl() != "" {
externalLink.SetUrl(a.hasher.AnonymizeURL(externalLink.GetUrl()))
}
return anypb.New(externalLink)
}
// Empty marker types - preserve as-is (no PII)
for _, marker := range []proto.Message{
&v2.SkipEntitlementsAndGrants{},
&v2.SkipGrants{},
&v2.SkipEntitlements{},
} {
if ann.MessageIs(marker) {
return ann, nil
}
}
// Unknown type - drop it
return nil, nil
}
func (a *Anonymizer) processResourceTypeAnnotation(ann *anypb.Any) (*anypb.Any, error) {
// ChildResourceType - anonymize resource type ID
childRT := &v2.ChildResourceType{}
if ann.MessageIs(childRT) {
if err := ann.UnmarshalTo(childRT); err != nil {
return nil, err
}
if childRT.GetResourceTypeId() != "" {
childRT.SetResourceTypeId(a.hasher.AnonymizeResourceType(childRT.GetResourceTypeId()))
}
anyMsg, err := anypb.New(childRT)
if err != nil {
return nil, err
}
return anyMsg, nil
}
// ExternalLink - anonymize URL
externalLink := &v2.ExternalLink{}
if ann.MessageIs(externalLink) {
if err := ann.UnmarshalTo(externalLink); err != nil {
return nil, err
}
if externalLink.GetUrl() != "" {
externalLink.SetUrl(a.hasher.AnonymizeURL(externalLink.GetUrl()))
}
anyMsg, err := anypb.New(externalLink)
if err != nil {
return nil, err
}
return anyMsg, nil
}
// Empty marker types - preserve as-is (no PII)
for _, marker := range []proto.Message{
&v2.SkipEntitlementsAndGrants{},
&v2.SkipGrants{},
&v2.SkipEntitlements{},
} {
if ann.MessageIs(marker) {
return ann, nil
}
}
// Unknown type - drop it
return nil, nil
}
🤖 Prompt for AI Agents
In pkg/anonymize/annotations.go around lines 32 to 70, the calls to
anypb.New(childRT) and anypb.New(externalLink) ignore the returned error; update
both calls to capture (out, err := anypb.New(...)) and if err != nil return nil,
err, otherwise return out, nil so marshaling failures are propagated instead of
dropped.


// anonymizeGrantAnnotations processes all annotations on a Grant,
// anonymizing known types and dropping unknown types.
func (a *Anonymizer) anonymizeGrantAnnotations(g *v2.Grant) error {
if g == nil {
return nil
}

var result []*anypb.Any
for _, ann := range g.GetAnnotations() {
if processed, err := a.processGrantAnnotation(ann); err != nil {
return err
} else if processed != nil {
result = append(result, processed)
}
}

g.SetAnnotations(result)
return nil
}

// processGrantAnnotation processes a single annotation from a Grant.
// Returns nil if the annotation should be dropped.
func (a *Anonymizer) processGrantAnnotation(ann *anypb.Any) (*anypb.Any, error) {
// GrantExpandable - anonymize entitlement and resource type IDs
expandable := &v2.GrantExpandable{}
if ann.MessageIs(expandable) {
if err := ann.UnmarshalTo(expandable); err != nil {
return nil, err
}
entitlementIDs := expandable.GetEntitlementIds()
for i, eid := range entitlementIDs {
if eid != "" {
entitlementIDs[i] = a.hasher.AnonymizeExternalID(eid)
}
}
expandable.SetEntitlementIds(entitlementIDs)

resourceTypeIDs := expandable.GetResourceTypeIds()
for i, rtid := range resourceTypeIDs {
if rtid != "" {
resourceTypeIDs[i] = a.hasher.AnonymizeResourceType(rtid)
}
}
expandable.SetResourceTypeIds(resourceTypeIDs)
return anypb.New(expandable)
}

// GrantMetadata - clear metadata
grantMetadata := &v2.GrantMetadata{}
if ann.MessageIs(grantMetadata) {
if err := ann.UnmarshalTo(grantMetadata); err != nil {
return nil, err
}
grantMetadata.ClearMetadata()
return anypb.New(grantMetadata)
}

// GrantImmutable - anonymize source_id, clear metadata
grantImmutable := &v2.GrantImmutable{}
if ann.MessageIs(grantImmutable) {
if err := ann.UnmarshalTo(grantImmutable); err != nil {
return nil, err
}
if grantImmutable.GetSourceId() != "" {
grantImmutable.SetSourceId(a.hasher.Hash(grantImmutable.GetSourceId()))
}
grantImmutable.ClearMetadata()
return anypb.New(grantImmutable)
}

// ExternalLink - anonymize URL
externalLink := &v2.ExternalLink{}
if ann.MessageIs(externalLink) {
if err := ann.UnmarshalTo(externalLink); err != nil {
return nil, err
}
if externalLink.GetUrl() != "" {
externalLink.SetUrl(a.hasher.AnonymizeURL(externalLink.GetUrl()))
}
return anypb.New(externalLink)
}

// Unknown type - drop it
return nil, nil
}
Comment on lines +74 to +156
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle errors from anypb.New().

The anypb.New() function returns (*anypb.Any, error), but errors are not checked at lines 116, 126, 139, and 151. This could lead to silent failures during marshaling.

Apply this diff to handle errors properly:

 		expandable.SetResourceTypeIds(resourceTypeIDs)
-		return anypb.New(expandable)
+		anyMsg, err := anypb.New(expandable)
+		if err != nil {
+			return nil, err
+		}
+		return anyMsg, nil
 	}
 
 	// GrantMetadata - clear metadata
 	grantMetadata := &v2.GrantMetadata{}
 	if ann.MessageIs(grantMetadata) {
 		if err := ann.UnmarshalTo(grantMetadata); err != nil {
 			return nil, err
 		}
 		grantMetadata.ClearMetadata()
-		return anypb.New(grantMetadata)
+		anyMsg, err := anypb.New(grantMetadata)
+		if err != nil {
+			return nil, err
+		}
+		return anyMsg, nil
 	}
 
 	// GrantImmutable - anonymize source_id, clear metadata
 	grantImmutable := &v2.GrantImmutable{}
 	if ann.MessageIs(grantImmutable) {
 		if err := ann.UnmarshalTo(grantImmutable); err != nil {
 			return nil, err
 		}
 		if grantImmutable.GetSourceId() != "" {
 			grantImmutable.SetSourceId(a.hasher.Hash(grantImmutable.GetSourceId()))
 		}
 		grantImmutable.ClearMetadata()
-		return anypb.New(grantImmutable)
+		anyMsg, err := anypb.New(grantImmutable)
+		if err != nil {
+			return nil, err
+		}
+		return anyMsg, nil
 	}
 
 	// ExternalLink - anonymize URL
 	externalLink := &v2.ExternalLink{}
 	if ann.MessageIs(externalLink) {
 		if err := ann.UnmarshalTo(externalLink); err != nil {
 			return nil, err
 		}
 		if externalLink.GetUrl() != "" {
 			externalLink.SetUrl(a.hasher.AnonymizeURL(externalLink.GetUrl()))
 		}
-		return anypb.New(externalLink)
+		anyMsg, err := anypb.New(externalLink)
+		if err != nil {
+			return nil, err
+		}
+		return anyMsg, nil
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (a *Anonymizer) anonymizeGrantAnnotations(g *v2.Grant) error {
if g == nil {
return nil
}
var result []*anypb.Any
for _, ann := range g.GetAnnotations() {
if processed, err := a.processGrantAnnotation(ann); err != nil {
return err
} else if processed != nil {
result = append(result, processed)
}
}
g.SetAnnotations(result)
return nil
}
// processGrantAnnotation processes a single annotation from a Grant.
// Returns nil if the annotation should be dropped.
func (a *Anonymizer) processGrantAnnotation(ann *anypb.Any) (*anypb.Any, error) {
// GrantExpandable - anonymize entitlement and resource type IDs
expandable := &v2.GrantExpandable{}
if ann.MessageIs(expandable) {
if err := ann.UnmarshalTo(expandable); err != nil {
return nil, err
}
entitlementIDs := expandable.GetEntitlementIds()
for i, eid := range entitlementIDs {
if eid != "" {
entitlementIDs[i] = a.hasher.AnonymizeExternalID(eid)
}
}
expandable.SetEntitlementIds(entitlementIDs)
resourceTypeIDs := expandable.GetResourceTypeIds()
for i, rtid := range resourceTypeIDs {
if rtid != "" {
resourceTypeIDs[i] = a.hasher.AnonymizeResourceType(rtid)
}
}
expandable.SetResourceTypeIds(resourceTypeIDs)
return anypb.New(expandable)
}
// GrantMetadata - clear metadata
grantMetadata := &v2.GrantMetadata{}
if ann.MessageIs(grantMetadata) {
if err := ann.UnmarshalTo(grantMetadata); err != nil {
return nil, err
}
grantMetadata.ClearMetadata()
return anypb.New(grantMetadata)
}
// GrantImmutable - anonymize source_id, clear metadata
grantImmutable := &v2.GrantImmutable{}
if ann.MessageIs(grantImmutable) {
if err := ann.UnmarshalTo(grantImmutable); err != nil {
return nil, err
}
if grantImmutable.GetSourceId() != "" {
grantImmutable.SetSourceId(a.hasher.Hash(grantImmutable.GetSourceId()))
}
grantImmutable.ClearMetadata()
return anypb.New(grantImmutable)
}
// ExternalLink - anonymize URL
externalLink := &v2.ExternalLink{}
if ann.MessageIs(externalLink) {
if err := ann.UnmarshalTo(externalLink); err != nil {
return nil, err
}
if externalLink.GetUrl() != "" {
externalLink.SetUrl(a.hasher.AnonymizeURL(externalLink.GetUrl()))
}
return anypb.New(externalLink)
}
// Unknown type - drop it
return nil, nil
}
func (a *Anonymizer) anonymizeGrantAnnotations(g *v2.Grant) error {
if g == nil {
return nil
}
var result []*anypb.Any
for _, ann := range g.GetAnnotations() {
if processed, err := a.processGrantAnnotation(ann); err != nil {
return err
} else if processed != nil {
result = append(result, processed)
}
}
g.SetAnnotations(result)
return nil
}
// processGrantAnnotation processes a single annotation from a Grant.
// Returns nil if the annotation should be dropped.
func (a *Anonymizer) processGrantAnnotation(ann *anypb.Any) (*anypb.Any, error) {
// GrantExpandable - anonymize entitlement and resource type IDs
expandable := &v2.GrantExpandable{}
if ann.MessageIs(expandable) {
if err := ann.UnmarshalTo(expandable); err != nil {
return nil, err
}
entitlementIDs := expandable.GetEntitlementIds()
for i, eid := range entitlementIDs {
if eid != "" {
entitlementIDs[i] = a.hasher.AnonymizeExternalID(eid)
}
}
expandable.SetEntitlementIds(entitlementIDs)
resourceTypeIDs := expandable.GetResourceTypeIds()
for i, rtid := range resourceTypeIDs {
if rtid != "" {
resourceTypeIDs[i] = a.hasher.AnonymizeResourceType(rtid)
}
}
expandable.SetResourceTypeIds(resourceTypeIDs)
anyMsg, err := anypb.New(expandable)
if err != nil {
return nil, err
}
return anyMsg, nil
}
// GrantMetadata - clear metadata
grantMetadata := &v2.GrantMetadata{}
if ann.MessageIs(grantMetadata) {
if err := ann.UnmarshalTo(grantMetadata); err != nil {
return nil, err
}
grantMetadata.ClearMetadata()
anyMsg, err := anypb.New(grantMetadata)
if err != nil {
return nil, err
}
return anyMsg, nil
}
// GrantImmutable - anonymize source_id, clear metadata
grantImmutable := &v2.GrantImmutable{}
if ann.MessageIs(grantImmutable) {
if err := ann.UnmarshalTo(grantImmutable); err != nil {
return nil, err
}
if grantImmutable.GetSourceId() != "" {
grantImmutable.SetSourceId(a.hasher.Hash(grantImmutable.GetSourceId()))
}
grantImmutable.ClearMetadata()
anyMsg, err := anypb.New(grantImmutable)
if err != nil {
return nil, err
}
return anyMsg, nil
}
// ExternalLink - anonymize URL
externalLink := &v2.ExternalLink{}
if ann.MessageIs(externalLink) {
if err := ann.UnmarshalTo(externalLink); err != nil {
return nil, err
}
if externalLink.GetUrl() != "" {
externalLink.SetUrl(a.hasher.AnonymizeURL(externalLink.GetUrl()))
}
anyMsg, err := anypb.New(externalLink)
if err != nil {
return nil, err
}
return anyMsg, nil
}
// Unknown type - drop it
return nil, nil
}
🤖 Prompt for AI Agents
In pkg/anonymize/annotations.go around lines 74 to 156, the calls to
anypb.New(...) at the end of each branch return (any, error) but the error is
ignored; update each of the four places (the GrantExpandable, GrantMetadata,
GrantImmutable and ExternalLink branches around lines ~116, ~126, ~139, ~151) to
capture the returned values, check the error and return it on failure, e.g. out,
err := anypb.New(...); if err != nil { return nil, err }; return out, nil.


// anonymizeEntitlementAnnotations processes all annotations on an Entitlement,
// anonymizing known types and dropping unknown types.
func (a *Anonymizer) anonymizeEntitlementAnnotations(e *v2.Entitlement) error {
if e == nil {
return nil
}

var result []*anypb.Any
for _, ann := range e.GetAnnotations() {
if processed, err := a.processEntitlementAnnotation(ann); err != nil {
return err
} else if processed != nil {
result = append(result, processed)
}
}

e.SetAnnotations(result)
return nil
}

// processEntitlementAnnotation processes a single annotation from an Entitlement.
// Returns nil if the annotation should be dropped.
func (a *Anonymizer) processEntitlementAnnotation(ann *anypb.Any) (*anypb.Any, error) {
// EntitlementImmutable - anonymize source_id, clear metadata
entitlementImmutable := &v2.EntitlementImmutable{}
if ann.MessageIs(entitlementImmutable) {
if err := ann.UnmarshalTo(entitlementImmutable); err != nil {
return nil, err
}
if entitlementImmutable.GetSourceId() != "" {
entitlementImmutable.SetSourceId(a.hasher.Hash(entitlementImmutable.GetSourceId()))
}
entitlementImmutable.ClearMetadata()
return anypb.New(entitlementImmutable)
}

// ExternalLink - anonymize URL
externalLink := &v2.ExternalLink{}
if ann.MessageIs(externalLink) {
if err := ann.UnmarshalTo(externalLink); err != nil {
return nil, err
}
if externalLink.GetUrl() != "" {
externalLink.SetUrl(a.hasher.AnonymizeURL(externalLink.GetUrl()))
}
return anypb.New(externalLink)
}

// Unknown type - drop it
return nil, nil
}
Comment on lines +160 to +208
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle errors from anypb.New().

The anypb.New() function returns (*anypb.Any, error), but errors are not checked at lines 191 and 203. This could lead to silent failures during marshaling.

Apply this diff to handle errors properly:

 		entitlementImmutable.ClearMetadata()
-		return anypb.New(entitlementImmutable)
+		anyMsg, err := anypb.New(entitlementImmutable)
+		if err != nil {
+			return nil, err
+		}
+		return anyMsg, nil
 	}
 
 	// ExternalLink - anonymize URL
 	externalLink := &v2.ExternalLink{}
 	if ann.MessageIs(externalLink) {
 		if err := ann.UnmarshalTo(externalLink); err != nil {
 			return nil, err
 		}
 		if externalLink.GetUrl() != "" {
 			externalLink.SetUrl(a.hasher.AnonymizeURL(externalLink.GetUrl()))
 		}
-		return anypb.New(externalLink)
+		anyMsg, err := anypb.New(externalLink)
+		if err != nil {
+			return nil, err
+		}
+		return anyMsg, nil
 	}
🤖 Prompt for AI Agents
In pkg/anonymize/annotations.go around lines 160-208, the calls to
anypb.New(entitlementImmutable) and anypb.New(externalLink) ignore the returned
error; update both sites to capture the returned (*anypb.Any, error), check the
error and return nil, err on failure, otherwise return the created *anypb.Any
and nil. Ensure you propagate any Unmarshal errors as before and do not change
the function signature.

144 changes: 144 additions & 0 deletions pkg/anonymize/anonymize.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Package anonymize provides functionality to anonymize c1z files by replacing
// personally identifiable information (PII) and sensitive data with deterministic,
// anonymized equivalents while preserving structural relationships.
package anonymize

import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"time"

"google.golang.org/protobuf/types/known/timestamppb"
)

// Config holds configuration options for the anonymization process.
type Config struct {
// Salt is used as a key for HMAC-based hashing to generate deterministic
// but unpredictable anonymized values. Different salts produce different outputs.
// Required.
Salt string
}

// defaultConfig returns a Config with sensible default values.
func defaultConfig() Config {
return Config{
Salt: "baton-anonymize-default-salt",
}
}

// Anonymizer handles the anonymization of c1z file data.
type Anonymizer struct {
config Config
hasher *Hasher
timestamp time.Time // Single timestamp used for all anonymized timestamps
}

// New creates a new Anonymizer with the given configuration.
func New(config Config) *Anonymizer {
if config.Salt == "" {
panic("salt is required")
}
return &Anonymizer{
config: config,
hasher: NewHasher(config.Salt),
timestamp: time.Now(),
}
}

// AnonymizedTimestamp returns the single timestamp used for all anonymized records.
func (a *Anonymizer) AnonymizedTimestamp() *timestamppb.Timestamp {
return timestamppb.New(a.timestamp)
}

// newWithDefaults creates a new Anonymizer with default configuration.
func newWithDefaults() *Anonymizer {
return New(defaultConfig())
}

// Hasher provides deterministic hashing for anonymization.
// It uses HMAC-SHA256 with a salt to generate consistent but unpredictable values.
type Hasher struct {
salt []byte
}

// NewHasher creates a new Hasher with the given salt.
func NewHasher(salt string) *Hasher {
return &Hasher{
salt: []byte(salt),
}
}

// Hash generates a deterministic hash of the input string.
// The same input with the same salt always produces the same output.
func (h *Hasher) Hash(input string) string {
mac := hmac.New(sha256.New, h.salt)
mac.Write([]byte(input))
return hex.EncodeToString(mac.Sum(nil))
}

// HashN generates a deterministic hash truncated to n characters.
func (h *Hasher) HashN(input string, n int) string {
hash := h.Hash(input)
if len(hash) < n {
return hash
}
return hash[:n]
}

// AnonymizeEmail generates an anonymized email address using the full hash digest, split around an @ symbol.
func (h *Hasher) AnonymizeEmail(email string) string {
e := h.HashN(email, 32)
return e[:len(e)/2] + "@" + e[len(e)/2:]
}

// AnonymizeDisplayName generates an anonymized display name using the full hash digest.
func (h *Hasher) AnonymizeDisplayName(name string) string {
return h.HashN(name, 16)
}

// AnonymizeLogin generates an anonymized login/username using the full hash digest.
func (h *Hasher) AnonymizeLogin(login string) string {
return h.HashN(login, max(32, len(login)))
}

// AnonymizeEmployeeID generates an anonymized employee ID using the full hash digest.
func (h *Hasher) AnonymizeEmployeeID(empID string) string {
return h.HashN(empID, max(32, len(empID)))
}

// AnonymizeResourceID generates an anonymized resource ID, preserving the original length.
func (h *Hasher) AnonymizeResourceID(resourceID string) string {
return h.HashN(resourceID, max(32, len(resourceID)))
}

// AnonymizeExternalID generates an anonymized external ID, preserving the original length.
func (h *Hasher) AnonymizeExternalID(externalID string) string {
return h.HashN(externalID, max(32, len(externalID)))
}

// AnonymizeURL generates an anonymized URL.
func (h *Hasher) AnonymizeURL(url string) string {
return "https://example.com/" + h.HashN(url, 16)
}

// AnonymizeGivenName generates an anonymized given name using the full hash digest.
func (h *Hasher) AnonymizeGivenName(name string) string {
return h.HashN(name, 16)
}

// AnonymizeFamilyName generates an anonymized family name using the full hash digest.
func (h *Hasher) AnonymizeFamilyName(name string) string {
return h.HashN(name, 16)
}

// AnonymizeMiddleName generates an anonymized middle name using the full hash digest.
func (h *Hasher) AnonymizeMiddleName(name string) string {
return h.HashN(name, 16)
}

// AnonymizeResourceType generates an anonymized resource type name using the full hash digest.
// Uses deterministic hashing so the same type always maps to the same value.
func (h *Hasher) AnonymizeResourceType(resourceType string) string {
return h.HashN(resourceType, 8)
}
Loading
Loading