The crew profile page passed SeafarerDocument.number to the client unmasked for all roles and all doc types, exposing full Aadhaar/PAN identity numbers to MPO / Manager / Site staff — contradicting the field's PII annotation and §6 / Roles-and-Permissions §3 (Aadhaar/PAN are gated to Accounts/SuperUser, same as the bank account number). - crew-pii.ts: add documentNumberValue(number, docType, role) — masks AADHAAR / PAN for non-privileged roles via the existing canViewFullBankEpf gate + maskTail; non-identity docs (passport, CDC, STCW…) pass through; preserves the string|null contract. - crew/[id]/page.tsx: mask the number server-side before it crosses to the client. - Tests: unit cases for the helper; an integration test that invokes the server component and asserts the documents prop is masked for MANAGER/SITE_STAFF/MPO and full for ACCOUNTS/SUPERUSER. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
67 lines
3 KiB
TypeScript
67 lines
3 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { maskTail, canViewFullBankEpf, canViewSalary, bankEpfValue, documentNumberValue } from "@/lib/crew-pii";
|
|
|
|
// PII visibility rules for the crew profile (Crewing-Implementation-Spec §6/§8.8).
|
|
describe("crew PII masking", () => {
|
|
describe("maskTail", () => {
|
|
it("keeps the last 4 by default", () => {
|
|
expect(maskTail("123456789")).toBe("•••• 6789");
|
|
});
|
|
it("renders — for empty values", () => {
|
|
expect(maskTail(null)).toBe("—");
|
|
expect(maskTail("")).toBe("—");
|
|
});
|
|
it("fully masks values at or under the visible length", () => {
|
|
expect(maskTail("12")).toBe("••••");
|
|
expect(maskTail("1234")).toBe("••••");
|
|
});
|
|
});
|
|
|
|
describe("canViewFullBankEpf", () => {
|
|
it("only Accounts and SuperUser see full bank/EPF", () => {
|
|
expect(canViewFullBankEpf("ACCOUNTS")).toBe(true);
|
|
expect(canViewFullBankEpf("SUPERUSER")).toBe(true);
|
|
expect(canViewFullBankEpf("MANAGER")).toBe(false);
|
|
expect(canViewFullBankEpf("MANNING")).toBe(false);
|
|
expect(canViewFullBankEpf("SITE_STAFF")).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("canViewSalary", () => {
|
|
it("hides salary from site staff only", () => {
|
|
expect(canViewSalary("SITE_STAFF")).toBe(false);
|
|
expect(canViewSalary("MANAGER")).toBe(true);
|
|
expect(canViewSalary("ACCOUNTS")).toBe(true);
|
|
expect(canViewSalary("MANNING")).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("bankEpfValue", () => {
|
|
it("shows full to Accounts, masked to others, — when empty", () => {
|
|
expect(bankEpfValue("123456789", "ACCOUNTS")).toBe("123456789");
|
|
expect(bankEpfValue("123456789", "MANAGER")).toBe("•••• 6789");
|
|
expect(bankEpfValue(null, "ACCOUNTS")).toBe("—");
|
|
});
|
|
});
|
|
|
|
describe("documentNumberValue", () => {
|
|
it("masks Aadhaar/PAN numbers for non-privileged roles", () => {
|
|
expect(documentNumberValue("123456789012", "AADHAAR", "MANAGER")).toBe("•••• 9012");
|
|
expect(documentNumberValue("123456789012", "AADHAAR", "MANNING")).toBe("•••• 9012");
|
|
expect(documentNumberValue("ABCDE1234F", "PAN", "SITE_STAFF")).toBe("•••• 234F");
|
|
});
|
|
it("shows Aadhaar/PAN in full to Accounts and SuperUser", () => {
|
|
expect(documentNumberValue("123456789012", "AADHAAR", "ACCOUNTS")).toBe("123456789012");
|
|
expect(documentNumberValue("ABCDE1234F", "PAN", "SUPERUSER")).toBe("ABCDE1234F");
|
|
});
|
|
it("does not restrict non-identity documents for any role", () => {
|
|
expect(documentNumberValue("P1234567", "PASSPORT", "SITE_STAFF")).toBe("P1234567");
|
|
expect(documentNumberValue("CDC-99", "CDC", "MANNING")).toBe("CDC-99");
|
|
expect(documentNumberValue("STCW-1", "STCW", "MANAGER")).toBe("STCW-1");
|
|
});
|
|
it("returns null for an empty number regardless of type/role", () => {
|
|
expect(documentNumberValue(null, "AADHAAR", "ACCOUNTS")).toBeNull();
|
|
expect(documentNumberValue("", "PASSPORT", "MANAGER")).toBeNull();
|
|
});
|
|
});
|
|
});
|