- gti-scheduling-mcp: MCP server com ZProAdapter e KlingoAdapter (agendar() completo pros dois) - Workflow n8n 'Agendamento - GChat - Z-PRO': webhook -> AI Agent -> MCP -> Z-PRO, testado com sucesso real - Documentacao: specs, plano, runbook de problemas reais e export do workflow Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
130 lines
4.0 KiB
TypeScript
130 lines
4.0 KiB
TypeScript
/**
|
|
* test/sanitize.test.ts
|
|
*
|
|
* Testes unitários para a sanitização recursiva de respostas.
|
|
*/
|
|
|
|
import { describe, it } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import {
|
|
REDACTED,
|
|
isSensitiveField,
|
|
sanitizeRecursively,
|
|
sanitizeText,
|
|
} from "../src/diagnostics/sanitize.js";
|
|
|
|
describe("isSensitiveField", () => {
|
|
it("reconhece nomes de campos sensíveis ignorando maiúsculas/minúsculas", () => {
|
|
assert.equal(isSensitiveField("name"), true);
|
|
assert.equal(isSensitiveField("patientName"), true);
|
|
assert.equal(isSensitiveField("NOME"), true);
|
|
assert.equal(isSensitiveField("phone"), true);
|
|
assert.equal(isSensitiveField("telefone"), true);
|
|
assert.equal(isSensitiveField("email"), true);
|
|
assert.equal(isSensitiveField("cpf"), true);
|
|
assert.equal(isSensitiveField("document"), true);
|
|
assert.equal(isSensitiveField("documento"), true);
|
|
assert.equal(isSensitiveField("token"), true);
|
|
assert.equal(isSensitiveField("authorization"), true);
|
|
assert.equal(isSensitiveField("password"), true);
|
|
assert.equal(isSensitiveField("senha"), true);
|
|
assert.equal(isSensitiveField("address"), true);
|
|
assert.equal(isSensitiveField("endereco"), true);
|
|
});
|
|
|
|
it("não reconhece campos não sensíveis", () => {
|
|
assert.equal(isSensitiveField("id"), false);
|
|
assert.equal(isSensitiveField("appointmentId"), false);
|
|
assert.equal(isSensitiveField("startAt"), false);
|
|
assert.equal(isSensitiveField("status"), false);
|
|
assert.equal(isSensitiveField("page"), false);
|
|
});
|
|
});
|
|
|
|
describe("sanitizeRecursively", () => {
|
|
it("mascara valores de campos sensíveis", () => {
|
|
const sanitized = sanitizeRecursively({
|
|
name: "Maria dos Santos",
|
|
phone: "75999999999",
|
|
email: "maria@example.com",
|
|
});
|
|
|
|
assert.equal(sanitized.name, REDACTED);
|
|
assert.equal(sanitized.phone, REDACTED);
|
|
assert.equal(sanitized.email, REDACTED);
|
|
});
|
|
|
|
it("percorre objetos e arrays aninhados", () => {
|
|
const sanitized = sanitizeRecursively({
|
|
data: [
|
|
{
|
|
patient: { name: "João", cpf: "12345678900" },
|
|
appointmentId: "abc-123",
|
|
},
|
|
],
|
|
});
|
|
|
|
assert.equal(sanitized.data[0].patient.name, REDACTED);
|
|
assert.equal(sanitized.data[0].patient.cpf, REDACTED);
|
|
assert.equal(sanitized.data[0].appointmentId, "abc-123");
|
|
});
|
|
|
|
it("mantém campos não sensíveis intactos", () => {
|
|
const sanitized = sanitizeRecursively({
|
|
id: "app-1",
|
|
status: "confirmed",
|
|
page: 1,
|
|
});
|
|
|
|
assert.equal(sanitized.id, "app-1");
|
|
assert.equal(sanitized.status, "confirmed");
|
|
assert.equal(sanitized.page, 1);
|
|
});
|
|
|
|
it("redige ocorrências de segredos mesmo em campos não sensíveis", () => {
|
|
const token = "segredo-super-secreto";
|
|
const sanitized = sanitizeRecursively(
|
|
{
|
|
message: `token=${token}`,
|
|
data: "x",
|
|
},
|
|
[token],
|
|
);
|
|
|
|
assert.ok(!JSON.stringify(sanitized).includes(token));
|
|
assert.ok(JSON.stringify(sanitized).includes(REDACTED));
|
|
});
|
|
|
|
it("não vaza o token na saída sanitizada", () => {
|
|
const token = "token-real-xyz";
|
|
const sanitized = sanitizeRecursively(
|
|
{
|
|
data: [{ patient: { name: "Maria", phone: "75999999999" }, token }],
|
|
},
|
|
[token],
|
|
);
|
|
|
|
assert.ok(!JSON.stringify(sanitized).includes(token));
|
|
assert.ok(!JSON.stringify(sanitized).includes("75999999999"));
|
|
assert.ok(!JSON.stringify(sanitized).includes("Maria"));
|
|
});
|
|
|
|
it("lida com valores primitivos e null", () => {
|
|
assert.equal(sanitizeRecursively(null), null);
|
|
assert.equal(sanitizeRecursively("texto", ["zzz"]), "texto");
|
|
assert.equal(sanitizeRecursively(42), 42);
|
|
});
|
|
});
|
|
|
|
describe("sanitizeText", () => {
|
|
it("redige segredos e limita o comprimento", () => {
|
|
const long = "a".repeat(700);
|
|
const result = sanitizeText(`token=${long}`, [long]);
|
|
|
|
assert.ok(!result.includes(long));
|
|
assert.ok(result.length <= 500);
|
|
assert.ok(result.includes(REDACTED));
|
|
});
|
|
});
|