ThatAIguyA world of ideas

THE CONSCIOUSNESS ATLAS
Read. Experiment. Follow the evidence.

ConsciousnessLearn · Explore · Joseph Anady

Janady13/missionmatch / internal/modules/org/service.go

Source identity, revision, and recovered contents.

GitHub sourceSource text recovered

Source identity

FieldRecorded value
Source typeGitHub source
Original recordJanady13/missionmatch / internal/modules/org/service.go
DateNot specified in source
RepositoryJanady13/missionmatch
Revision2178e7298d0e5d9af83afa12402fd587f0ba6ed8
Branchfeature/aioe-upgrade
Path or archive memberinternal/modules/org/service.go
Original SHA-256bb37586dbbe97b8fd25cfc7a579e55163d4d5550a9aca2fb7db826ab25edf0e1
Git blob SHA1fd4decd3d6fe5bd7bf174d2f0b53bafd515c0a6
Reading-copy SHA-256bb37586dbbe97b8fd25cfc7a579e55163d4d5550a9aca2fb7db826ab25edf0e1

Original source ↗

Recovered source transcript

// Package org serves per organization branding so the application white labels for every client that // buys it. missionMATCH is sold to more than one client, so the mark in the shell is tenant data read // from the organizations row, never anything hard coded. // // The security posture of this package is set by one fact: the logo is bytes uploaded by a customer and // then served from the application origin. That makes it the only untrusted binary the app will ever // return, so it is treated as hostile until proven otherwise. // // 1. The declared Content-Type from the client is IGNORED. The format is derived from the bytes by // image.DecodeConfig, which reads only the header and fails on anything that is not a real raster // image. An SVG or an HTML document renamed to logo.png does not decode and is rejected. // 2. SVG is not accepted in any form. An SVG served same origin can carry a script element and become // stored cross site scripting against every user in that organization. // 3. Size and dimensions are bounded in Go before the insert, and again by CHECK constraints in // migration 0046, so a bad row is impossible from either direction. package org import ( "bytes" "context" "crypto/sha256" "encoding/hex" "errors" "fmt" "image" // Registering the decoders is what makes image.DecodeConfig able to identify these formats. Only // these two are registered on purpose: GIF can be animated and SVG is not a raster format at all. _ "image/jpeg" _ "image/png" "strings" "time" "github.com/jackc/pgx/v5" "github.com/missionworkforce/missionmatch/internal/platform/audit" "github.com/missionworkforce/missionmatch/internal/platform/tenant" ) const ( // MaxLogoBytes matches the CHECK in migration 0046. Far beyond any real logo, small enough that a // tenant cannot bloat the row or the backups. MaxLogoBytes = 512 * 1024 // MaxLogoDimension guards a decompression bomb. DecodeConfig reads only the header, so a small file // can still declare enormous dimensions; a downstream resize would then allocate that much memory. MaxLogoDimension = 4096 // MaxDisplayName matches the CHECK in migration 0046. MaxDisplayName = 120 ) var ( ErrNoDatabase = errors.New("org: no database configured") ErrNoLogo = errors.New("org: no logo set") ErrLogoTooLarge = errors.New("org: logo exceeds the maximum size") ErrLogoFormat = errors.New("org: logo must be a PNG or JPEG image") ErrLogoTooBig = errors.New("org: logo dimensions exceed the maximum") ErrDisplayName = errors.New("org: display name must be between 1 and 120 characters") ) // formatMIME maps the format name image.DecodeConfig reports onto the MIME we store and later serve. // The map is the allowlist: a format that is not a key here cannot be stored, so adding a format is a // deliberate edit rather than an accident of which decoders happen to be linked in. var formatMIME = map[string]string{ "png": "image/png", "jpeg": "image/jpeg", } // Branding is the tenant's brand identity for the application shell. type Branding struct { OrganizationID string `json:"organization_id"` Name string `json:"name"` DisplayName string `json:"display_name"` HasLogo bool `json:"has_logo"` LogoETag string `json:"logo_etag,omitempty"` LogoUpdatedAt string `json:"logo_updated_at,omitempty"` } // Logo is the stored image plus the metadata needed to serve and cache it. type Logo struct { Bytes []byte MIME string ETag string } type Service struct { db *tenant.DB audit *audit.Writer clock func() time.Time } func NewService(db *tenant.DB) *Service { return &Service{db: db, clock: time.Now} } func NewServiceWithAudit(db *tenant.DB, aud *audit.Writer) *Service { return &Service{db: db, audit: aud, clock: time.Now} } // etagFor is a strong validator over the exact bytes served. Deriving it from the content rather than // from a timestamp means a rollback to a previous logo still busts the cache correctly. func etagFor(b []byte) string { sum := sha256.Sum256(b) return `"` + hex.EncodeToString(sum[:16]) + `"` } // Branding reads the tenant's brand. It never returns the logo bytes, so the shell can decide whether to // request the image without paying for it on every page load. func (s *Service) Branding(ctx context.Context) (Branding, error) { if s.db == nil { return Branding{}, ErrNoDatabase } orgID, _ := tenant.OrgFromContext(ctx) out := Branding{OrganizationID: orgID} err := s.db.RunInTenantTx(ctx, func(ctx context.Context, tx pgx.Tx) error { var display *string var logo []byte var updated *time.Time // RLS restricts organizations to the caller's own row, so this reads one row without a predicate // on id. A missing row means the GUC is wrong, which RunInTenantTx already refuses. err := tx.QueryRow(ctx, `SELECT name, display_name, logo, logo_updated_at FROM organizations`). Scan(&out.Name, &display, &logo, &updated) if err != nil { return fmt.Errorf("read branding: %w", err) } if display != nil { out.DisplayName = *display } if len(logo) > 0 { out.HasLogo = true out.LogoETag = etagFor(logo) } if updated != nil { out.LogoUpdatedAt = updated.UTC().Format(time.RFC3339) } return nil }) if err != nil { return Branding{}, err } return out, nil } // Logo returns the stored image. The MIME comes from what the server derived at upload time, never from // anything a caller supplied. func (s *Service) Logo(ctx context.Context) (Logo, error) { if s.db == nil { return Logo{}, ErrNoDatabase } var out Logo err := s.db.RunInTenantTx(ctx, func(ctx context.Context, tx pgx.Tx) error { var b []byte var mime *string if err := tx.QueryRow(ctx, `SELECT logo, logo_mime FROM organizations`).Scan(&b, &mime); err != nil { return fmt.Errorf("read logo: %w", err) } if len(b) == 0 || mime == nil { return ErrNoLogo } out = Logo{Bytes: b, MIME: *mime, ETag: etagFor(b)} return nil }) if err != nil { return Logo{}, err } return out, nil } // SetLogo validates and stores an uploaded image. // // Validation order matters and is deliberate: cheap checks first so a hostile upload is rejected before // any decoding work happens. func (s *Service) SetLogo(ctx context.Context, raw []byte) (Branding, error) { if s.db == nil { return Branding{}, ErrNoDatabase } if len(raw) == 0 { return Branding{}, ErrLogoFormat } if len(raw) > MaxLogoBytes { return Branding{}, ErrLogoTooLarge } // The authoritative check. DecodeConfig reads only the header, so this is cheap and allocates // nothing proportional to the declared dimensions. It fails on SVG, on HTML, and on any file whose // extension or declared type disagrees with its actual content. cfg, format, err := image.DecodeConfig(bytes.NewReader(raw)) if err != nil { return Branding{}, ErrLogoFormat } mime, ok := formatMIME[format] if !ok { return Branding{}, ErrLogoFormat } if cfg.Width <= 0 || cfg.Height <= 0 || cfg.Width > MaxLogoDimension || cfg.Height > MaxLogoDimension { return Branding{}, ErrLogoTooBig } now := s.clock().UTC() err = s.db.RunInTenantTx(ctx, func(ctx context.Context, tx pgx.Tx) error { ct, err := tx.Exec(ctx, `UPDATE organizations SET logo = $1, logo_mime = $2, logo_updated_at = $3`, raw, mime, now) if err != nil { return fmt.Errorf("store logo: %w", err) } if ct.RowsAffected() != 1 { return fmt.Errorf("org: expected to update exactly one organization, updated %d", ct.RowsAffected()) } return nil }) if err != nil { return Branding{}, err } return s.Branding(ctx) } // ClearLogo removes the stored image and falls the shell back to the product mark. func (s *Service) ClearLogo(ctx context.Context) (Branding, error) { if s.db == nil { return Branding{}, ErrNoDatabase } err := s.db.RunInTenantTx(ctx, func(ctx context.Context, tx pgx.Tx) error { _, err := tx.Exec(ctx, `UPDATE organizations SET logo = NULL, logo_mime = NULL, logo_updated_at = NULL`) if err != nil { return fmt.Errorf("clear logo: %w", err) } return nil }) if err != nil { return Branding{}, err } return s.Branding(ctx) } // SetDisplayName sets the brand name shown in the shell. An empty value clears it and falls back to the // organization name, which is why blank is accepted here but a blank string is never stored. func (s *Service) SetDisplayName(ctx context.Context, name string) (Branding, error) { if s.db == nil { return Branding{}, ErrNoDatabase } trimmed := strings.TrimSpace(name) if len(trimmed) > MaxDisplayName { return Branding{}, ErrDisplayName } err := s.db.RunInTenantTx(ctx, func(ctx context.Context, tx pgx.Tx) error { var v any if trimmed != "" { v = trimmed } if _, err := tx.Exec(ctx, `UPDATE organizations SET display_name = $1`, v); err != nil { return fmt.Errorf("store display name: %w", err) } return nil }) if err != nil { return Branding{}, err } return s.Branding(ctx) }

Search equations, builds, manuscript text, and recovered sources.