Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / internal/modules/matching/slate_service.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | internal/modules/matching/slate_service.go |
| Original SHA-256 | 0267d6bf3991f5a3f4ac039d02cf9f2f4dfcce04f149b87eeee7c22173156155 |
| Git blob SHA | bc101da5a3fc4c271dc5a0c8e905999d50204505 |
| Reading-copy SHA-256 | 0267d6bf3991f5a3f4ac039d02cf9f2f4dfcce04f149b87eeee7c22173156155 |
Every recorded occurrence
These 2 records contain identical original source bytes. Their paths and revisions remain part of the archive.
| Original record | Branch or archive | Revision / identifier | Original location |
|---|---|---|---|
| Janady13/missionmatch / internal/modules/matching/slate_service.go | feature/opportunity-to-outcome | 720d47fae476b0d38477ae4401697d372b1ea2bc | Open original |
| Janady13/missionmatch / internal/modules/matching/slate_service.go | feature/aioe-upgrade | 2178e7298d0e5d9af83afa12402fd587f0ba6ed8 | Open original |
Recovered source transcript
Download complete source text ↓299 lines · 9.9 KB
package matching
import (
"context"
"encoding/csv"
"encoding/json"
"errors"
"strconv"
"strings"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/shopspring/decimal"
"github.com/missionworkforce/missionmatch/internal/modules/matching/engine"
"github.com/missionworkforce/missionmatch/internal/platform/audit"
"github.com/missionworkforce/missionmatch/internal/platform/csvsafe"
"github.com/missionworkforce/missionmatch/internal/platform/database/dbgen"
)
// ListSlates returns the slates generated for one requirement, newest first, with a member count
// (slateMATCH slate list). It reads the stored slates, it does not rescore.
func (s *Service) ListSlates(ctx context.Context, requirementID uuid.UUID) ([]SlateSummaryView, error) {
if s.db == nil {
return nil, ErrNoDatabase
}
var out []SlateSummaryView
err := s.db.RunInTenantTx(ctx, func(ctx context.Context, tx pgx.Tx) error {
q := dbgen.New(tx)
slates, e := q.ListSlatesForRequirement(ctx, requirementID)
if e != nil {
return e
}
out = make([]SlateSummaryView, 0, len(slates))
for _, sl := range slates {
members, e := q.ListSlateMembers(ctx, sl.ID)
if e != nil {
return e
}
out = append(out, SlateSummaryView{
SlateID: sl.ID.String(),
Name: sl.Name,
Status: sl.Status,
MemberCount: len(members),
CreatedAt: sl.CreatedAt.Format("2006-01-02"),
})
}
return nil
})
return out, err
}
// SlateDetail returns one slate with its ranked members (read from the persisted slate_members, never
// rescored) and the proposal readiness rollup. ErrNotFound when the slate is not in the caller's org.
func (s *Service) SlateDetail(ctx context.Context, slateID uuid.UUID) (SlateDetailView, error) {
if s.db == nil {
return SlateDetailView{}, ErrNoDatabase
}
var view SlateDetailView
err := s.db.RunInTenantTx(ctx, func(ctx context.Context, tx pgx.Tx) error {
q := dbgen.New(tx)
sl, e := q.GetSlate(ctx, slateID)
if errors.Is(e, pgx.ErrNoRows) {
return ErrNotFound
}
if e != nil {
return e
}
members, e := q.ListSlateMembersWithNames(ctx, slateID)
if e != nil {
return e
}
title := ""
if rq, e := q.GetRequirement(ctx, sl.RequirementID); e == nil {
title = rq.Title
} else if !errors.Is(e, pgx.ErrNoRows) {
// A missing requirement leaves the title blank; a real error must not be swallowed into a
// silently blank Requirement line on a branded export.
return e
}
view = SlateDetailView{
SlateID: sl.ID.String(),
RequirementID: sl.RequirementID.String(),
RequirementTitle: title,
Name: sl.Name,
Status: sl.Status,
Members: make([]SlateMemberDetailView, len(members)),
Readiness: computeReadiness(sl.ID.String(), sl.RequirementID.String(), members),
}
for i, m := range members {
view.Members[i] = slateMemberDetailView(m)
}
return nil
})
return view, err
}
// ProposalReadiness returns the readiness rollup for one slate (GET /proposal-readiness): the band and
// human decision mix across its members, computed from the stored slate and never by rescoring.
func (s *Service) ProposalReadiness(ctx context.Context, slateID uuid.UUID) (ProposalReadinessView, error) {
if s.db == nil {
return ProposalReadinessView{}, ErrNoDatabase
}
var out ProposalReadinessView
err := s.db.RunInTenantTx(ctx, func(ctx context.Context, tx pgx.Tx) error {
q := dbgen.New(tx)
sl, e := q.GetSlate(ctx, slateID)
if errors.Is(e, pgx.ErrNoRows) {
return ErrNotFound
}
if e != nil {
return e
}
members, e := q.ListSlateMembersWithNames(ctx, slateID)
if e != nil {
return e
}
out = computeReadiness(sl.ID.String(), sl.RequirementID.String(), members)
return nil
})
return out, err
}
// ExportSlate renders a slate as a branded CSV (the proposal candidate slate a capture team submits).
// It reads the persisted member scores and decisions, so the export always matches the frozen slate.
func (s *Service) ExportSlate(ctx context.Context, slateID uuid.UUID) (string, error) {
if s.db == nil {
return "", ErrNoDatabase
}
var out string
err := s.db.RunInTenantTx(ctx, func(ctx context.Context, tx pgx.Tx) error {
q := dbgen.New(tx)
sl, e := q.GetSlate(ctx, slateID)
if errors.Is(e, pgx.ErrNoRows) {
return ErrNotFound
}
if e != nil {
return e
}
members, e := q.ListSlateMembersWithNames(ctx, slateID)
if e != nil {
return e
}
title := ""
if rq, e := q.GetRequirement(ctx, sl.RequirementID); e == nil {
title = rq.Title
} else if !errors.Is(e, pgx.ErrNoRows) {
// A missing requirement leaves the title blank; a real error must not be swallowed into a
// silently blank Requirement line on a branded export.
return e
}
out = renderSlateCSV(sl, title, members)
// The export is a bulk egress of candidate PII (names, scores, decisions). Record it in the
// append-only audit with the slate and the actor only, NEVER the reviewer note text or scores.
payload, _ := json.Marshal(map[string]any{"members": len(members)})
return s.auditOnTx(ctx, tx, audit.Event{Action: "aedt.slate.exported", EntityType: "slate", EntityID: sl.ID.String(), NewValues: payload})
})
return out, err
}
// computeReadiness rolls a slate's persisted members into the proposal readiness view: the band mix,
// the human decision mix, the approved and proposal ready count, and an overall label. Pure.
func computeReadiness(slateID, requirementID string, members []dbgen.ListSlateMembersWithNamesRow) ProposalReadinessView {
r := ProposalReadinessView{SlateID: slateID, RequirementID: requirementID, TotalMembers: len(members)}
for _, m := range members {
switch m.Decision {
case "approved":
r.Approved++
case "rejected":
r.Rejected++
default:
r.Pending++
}
switch m.Band {
case "green":
r.ProposalReady++
case "yellow":
r.Potential++
default:
r.Recruiting++
}
if m.Decision == "approved" && m.Band == "green" {
r.ApprovedReady++
}
}
r.ReadinessPct = "0"
if r.TotalMembers > 0 {
// Approved share as a percent, decimal so the rollup carries no float64.
pct := decimal.NewFromInt(int64(r.Approved)).
Div(decimal.NewFromInt(int64(r.TotalMembers))).
Mul(decimal.NewFromInt(100))
r.ReadinessPct = pct.StringFixed(0)
}
r.Status = readinessStatus(r)
return r
}
func readinessStatus(r ProposalReadinessView) string {
switch {
case r.TotalMembers == 0:
return "Empty"
case r.Pending > 0:
return "In Review"
case r.Approved == 0:
return "Needs Recruiting"
default:
return "Proposal Ready"
}
}
func slateMemberDetailView(m dbgen.ListSlateMembersWithNamesRow) SlateMemberDetailView {
rank := 0
if m.Rank != nil {
rank = int(*m.Rank)
}
note := ""
if m.DecisionNote != nil {
note = *m.DecisionNote
}
var gaps []string
if len(m.GapSummary) > 0 {
_ = json.Unmarshal(m.GapSummary, &gaps)
}
return SlateMemberDetailView{
CandidateID: m.CandidateID.String(),
FullName: m.FullName,
Rank: rank,
Total: m.TotalScore.StringFixed(2),
Band: m.Band,
BandLabel: bandLabel(m.Band),
Components: map[string]string{
"Clearance": m.ClearanceScore.StringFixed(2),
"Skills": m.SkillsScore.StringFixed(2),
"Certifications": m.CertsScore.StringFixed(2),
"CustomerExperience": m.CustomerScore.StringFixed(2),
"Experience": m.ExperienceScore.StringFixed(2),
"LocationAvailability": m.LocationScore.StringFixed(2),
},
Explanation: m.Explanation,
GapSummary: gaps,
Decision: m.Decision,
DecisionNote: note,
}
}
// bandLabel maps a stored band code to its human label, defaulting safely on an unknown code.
func bandLabel(code string) string {
if b, err := engine.ParseBand(code); err == nil {
return b.Label()
}
return code
}
// renderSlateCSV writes a branded proposal candidate slate. The header carries the product and slate
// identity and the standing clearance disclaimer; the rows are the ranked, frozen members with their
// human decisions. encoding/csv quotes any name or note that contains a comma.
func renderSlateCSV(sl dbgen.Slate, requirementTitle string, members []dbgen.ListSlateMembersWithNamesRow) string {
var b strings.Builder
b.WriteString("missionMATCH slateMATCH Proposal Candidate Slate\n")
// csvsafe.Cell, not csvField. csvField only adds RFC 4180 quoting when a value contains a comma, a
// quote, or a newline; it does not neutralise the =+-@ and tab and carriage return prefixes that make a
// spreadsheet treat a cell as a FORMULA. The data rows below were given csvsafe treatment and these two
// header lines were missed, which left the exploit fully open: a slate named =cmd|'/c calc.exe'!A1
// contains no comma, so csvField passed it through verbatim, and this file is emailed to a government
// customer by the module's own threat model. The payload runs when they open it.
b.WriteString("Slate," + csvField(csvsafe.Cell(sl.Name)) + "\n")
b.WriteString("Requirement," + csvField(csvsafe.Cell(requirementTitle)) + "\n")
b.WriteString("Clearance shown is a self reported indicator, never verified.\n")
b.WriteString("\n")
w := csv.NewWriter(&b)
_ = w.Write([]string{"Rank", "Candidate", "Total Score", "Band", "Decision", "Reviewer Note", "Explanation"})
for _, m := range members {
rank := ""
if m.Rank != nil {
rank = strconv.Itoa(int(*m.Rank))
}
note := ""
if m.DecisionNote != nil {
note = *m.DecisionNote
}
// Every cell passes through the shared guard. A candidate name is parsed from a resume, so it is
// attacker controlled, and this file is emailed to a client who opens it in a spreadsheet.
_ = w.Write(csvsafe.Row([]string{
rank, m.FullName, m.TotalScore.StringFixed(2), bandLabel(m.Band), m.Decision, note, m.Explanation,
}))
}
w.Flush()
return b.String()
}
// csvField quotes a single header value that may contain a comma (the csv writer handles the rows).
func csvField(s string) string {
if strings.ContainsAny(s, ",\"\n") {
return "\"" + strings.ReplaceAll(s, "\"", "\"\"") + "\""
}
return s
}