Compliance para devs: LGPD e GDPR traduzidos em decisões de código

1. Fundamentos Legais e Mapeamento de Dados no Código

A implementação de compliance começa com a tradução de bases legais em variáveis de estado. Cada operação de tratamento deve estar associada a uma base legal específica, controlada por enums e verificada em tempo de execução.

enum BaseLegal {
  CONSENTIMENTO = "consentimento",
  LEGITIMO_INTERESSE = "legitimo_interesse",
  EXECUCAO_CONTRATUAL = "execucao_contratual",
  OBRIGACAO_LEGAL = "obrigacao_legal"
}

class DadosPessoais {
  constructor(dado, baseLegal, finalidade) {
    this.dado = dado;
    this.baseLegal = baseLegal;
    this.finalidade = finalidade;
    this.dataColeta = new Date().toISOString();
  }
}

O mapeamento de fluxos exige um inventário automatizado. Use anotações em decorators para marcar campos sensíveis:

function DadoPessoal(categoria, finalidade) {
  return function(target, propertyKey) {
    if (!target.__inventario) target.__inventario = [];
    target.__inventario.push({
      campo: propertyKey,
      categoria: categoria,
      finalidade: finalidade
    });
  };
}

class Usuario {
  @DadoPessoal("identificacao", "cadastro")
  cpf;

  @DadoPessoal("contato", "comunicacao")
  email;
}

A diferenciação entre controlador (quem decide) e operador (quem executa) deve refletir em camadas de arquitetura: serviços internos como controladores, APIs de terceiros como operadores.

2. Consentimento e Gerenciamento de Preferências do Usuário

O consentimento granular exige persistência com metadados de versão:

CREATE TABLE consentimentos (
  id UUID PRIMARY KEY,
  usuario_id UUID NOT NULL,
  finalidade VARCHAR(100) NOT NULL,
  concedido BOOLEAN NOT NULL,
  versao_termo VARCHAR(20) NOT NULL,
  timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  ip_origem VARCHAR(45),
  UNIQUE(usuario_id, finalidade)
);

A revogação deve disparar uma rotina de tratamento:

function revogarConsentimento(usuarioId, finalidade) {
  db.exec("UPDATE consentimentos SET concedido = false WHERE usuario_id = ? AND finalidade = ?", [usuarioId, finalidade]);

  // Dispara job para anonimizar dados daquela finalidade
  jobQueue.add("anonimizar_dados", {
    usuarioId: usuarioId,
    finalidade: finalidade
  });
}

Logs de consentimento com timestamp e versão do termo garantem auditabilidade:

function registrarConsentimento(usuarioId, finalidade, concedido, versao) {
  const log = {
    usuarioId,
    finalidade,
    concedido,
    versao,
    timestamp: new Date().toISOString(),
    userAgent: navigator.userAgent
  };

  db.collection("logs_consentimento").insertOne(log);
}

3. Minimização de Dados e Retenção Programada

Schemas de banco devem refletir o princípio da minimização:

CREATE TABLE pedidos (
  id UUID PRIMARY KEY,
  valor_total DECIMAL(10,2),
  data_pedido DATE,
  -- NUNCA armazenar dados de cartão de crédito aqui
  -- Apenas referência ao token do gateway
  token_pagamento VARCHAR(100)
);

Políticas de retenção com jobs agendados:

// Job cron: executa toda madrugada
function jobExpiracaoDados() {
  const dataLimite = new Date();
  dataLimite.setMonth(dataLimite.getMonth() - 6); // 6 meses de retenção

  db.exec(
    "DELETE FROM logs_navegacao WHERE data_hora < ?",
    [dataLimite.toISOString()]
  );

  db.exec(
    "UPDATE usuarios SET dados_navegacao = NULL WHERE ultimo_acesso < ?",
    [dataLimite.toISOString()]
  );
}

Anonimização com hash e salting:

function pseudonimizarCPF(cpf) {
  const salt = crypto.randomBytes(16).toString("hex");
  const hash = crypto.createHash("sha256").update(cpf + salt).digest("hex");
  return { hash, salt };
}

4. Direitos dos Titulares: Acesso, Retificação e Exclusão

Endpoint de exportação de dados:

app.get("/api/v1/dados/exportar", async (req, res) => {
  const usuario = await db.collection("usuarios").findOne({ _id: req.userId });
  const pedidos = await db.collection("pedidos").find({ usuario_id: req.userId }).toArray();

  const dossie = {
    dadosCadastrais: usuario,
    historicoPedidos: pedidos,
    logsConsentimento: await db.collection("logs_consentimento").find({ usuarioId: req.userId }).toArray(),
    dataExportacao: new Date().toISOString()
  };

  res.setHeader("Content-Type", "application/json");
  res.setHeader("Content-Disposition", "attachment; filename=dados_pessoais.json");
  res.json(dossie);
});

Exclusão lógica com rastreabilidade:

function softDeleteUsuario(usuarioId) {
  db.collection("usuarios").updateOne(
    { _id: usuarioId },
    { $set: { 
      deleted_at: new Date().toISOString(),
      deleted_by: "solicitacao_titular",
      motivo: "direito_ao_esquecimento"
    }}
  );

  // Job assíncrono para exclusão física após 30 dias
  jobQueue.add("exclusao_fisica", { usuarioId, agendarPara: "30d" });
}

5. Segurança de Dados e Notificação de Violações

Criptografia em repouso com AES-256:

const algoritmo = "aes-256-gcm";
const chave = process.env.CHAVE_CRIPTOGRAFIA;

function criptografar(texto) {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv(algoritmo, Buffer.from(chave, "hex"), iv);
  let encrypted = cipher.update(texto, "utf8", "hex");
  encrypted += cipher.final("hex");
  return iv.toString("hex") + ":" + encrypted;
}

Detecção de vazamento com rate limiting e logs anômalos:

function detectarVazamento(logEntry) {
  const anomalias = [];

  if (logEntry.camposAcessados.length > 50) {
    anomalias.push("ACESSO_MACICO_DADOS");
  }

  if (logEntry.horario < "06:00" || logEntry.horario > "22:00") {
    anomalias.push("HORARIO_ATIPICO");
  }

  if (anomalias.length > 0) {
    notificarDPO(anomalias, logEntry);
  }
}

Trigger de notificação para ANPD:

function notificarViolacao(detalhes) {
  const template = {
    dataOcorrencia: new Date().toISOString(),
    natureza: detalhes.tipo,
    categoriasDados: detalhes.dadosAfetados,
    numeroTitulares: detalhes.quantidadeUsuarios,
    medidasMitigadoras: detalhes.acoesTomadas,
    contatoDPO: "dpo@empresa.com.br"
  };

  emailService.enviar("anpd@anpd.gov.br", "Notificação de Violação", template);
  emailService.enviar("dpo@empresa.com.br", "Alerta Interno", detalhes);
}

6. Transferência Internacional e Armazenamento Regional

Lógica condicional para escolha de cluster de banco:

function obterCluster(regiaoUsuario) {
  const clusters = {
    "BR": "sa-east-1",
    "EU": "eu-west-1",
    "US": "us-east-1"
  };

  return clusters[regiaoUsuario] || "sa-east-1";
}

async function salvarDados(usuario, dados) {
  const cluster = obterCluster(usuario.regiao);
  const db = await conectarCluster(cluster);
  await db.collection("dados_pessoais").insertOne(dados);
}

Validação automática de SCCs em integrações:

function validarIntegracao(parceiro) {
  if (parceiro.pais !== "BR" && !parceiro.sccAssinado) {
    throw new Error("Transferência internacional sem SCCs válidas");
  }

  if (parceiro.pais === "US" && !parceiro.privacyShield) {
    throw new Error("Empresa dos EUA sem certificação Privacy Shield");
  }
}

7. Auditoria, Logs e Documentação Automatizada

Logs imutáveis de acesso a dados pessoais:

CREATE TABLE logs_acesso (
  id SERIAL PRIMARY KEY,
  usuario_id UUID NOT NULL,
  campo_acessado VARCHAR(100) NOT NULL,
  finalidade VARCHAR(200) NOT NULL,
  timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  ip_origem VARCHAR(45),
  user_agent TEXT,
  -- Trigger impede UPDATE/DELETE
  CHECK (1=1) -- Imutabilidade garantida por trigger no banco
);

-- Trigger de imutabilidade
CREATE OR REPLACE FUNCTION impedir_alteracao_log()
RETURNS TRIGGER AS $$
BEGIN
  RAISE EXCEPTION 'Logs de acesso são imutáveis';
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_imutavel
BEFORE UPDATE OR DELETE ON logs_acesso
FOR EACH ROW EXECUTE FUNCTION impedir_alteracao_log();

Geração automática de RAT via CI/CD:

# script/gerar_rat.js
const inventario = require("./inventario_dados.json");

function gerarRAT() {
  const rat = {
    dataGeracao: new Date().toISOString(),
    versao: process.env.CI_COMMIT_SHA,
    controlador: "Empresa X Ltda",
    encarregado: "dpo@empresa.com.br",
    operacoes: inventario.map(op => ({
      finalidade: op.finalidade,
      baseLegal: op.baseLegal,
      categoriasDados: op.campos,
      prazoRetencao: op.retencao,
      medidasSeguranca: ["criptografia_aes256", "tls13", "logs_imutaveis"]
    }))
  };

  fs.writeFileSync("rat_automatico.json", JSON.stringify(rat, null, 2));
}

Testes unitários de compliance:

describe("Regras de Privacidade", () => {
  it("CPF não deve aparecer em logs de texto claro", () => {
    const log = gerarLogAcesso({ cpf: "123.456.789-00" });
    expect(log.mensagem).not.toContain("123.456.789-00");
    expect(log.mensagem).toContain("***.***.***-**");
  });

  it("consentimento deve ser registrado antes de qualquer tratamento", () => {
    const usuario = new Usuario({ email: "teste@teste.com" });
    expect(() => usuario.processarDados()).toThrow("Consentimento não registrado");
  });
});

Referências