Auditoria prática: acesso, dados e contestação
Este laboratório audita uma tela sintética de conclusão da OS 742 e produz um pacote de evidências sem dados pessoais reais. O leitor abre uma jornada deliberadamente inacessível, testa teclado e zoom, inventaria campos enviados ao ERP e examina uma recomendação de prioridade feita por IA. Um validador Python detecta botão não semântico, rótulo ausente, feedback inacessível, localização sem finalidade, retenção indefinida e contestação sem autoridade. Depois das correções, o mesmo comando passa; duas mutações reproduzem as falhas. O artefato final documenta limites: o teste estrutural não comprova conformidade WCAG, base legal nem ausência de viés, mas torna controles, pendências, responsáveis e critérios de aceite reproduzíveis.
Auditoria prática: acesso, dados e contestação
Missão, hipótese e limites
Missão: avaliar uma tela fictícia que conclui a OS 742, um inventário de dados enviado ao ERP e uma recomendação de prioridade por IA. Você começará com falhas conhecidas, obterá resultado vermelho, corrigirá e provará resultado verde.
Hipótese: requisitos estruturais, evidências manuais e documentação de governança conseguem detectar barreiras e decisões incompletas antes da produção. O validador não decide conformidade WCAG, justiça ou base legal; ele impede que campos obrigatórios e falhas deliberadas desapareçam do relatório.
Ambiente: Python 3.11 ou superior, navegador moderno, editor e terminal. Duração: 120–180 minutos. Use somente os dados sintéticos abaixo. Não inclua nome, CPF, matrícula, localização, comentário ou credencial real.
Neste laboratório, auditar não significa procurar culpados nem declarar que o produto inteiro está conforme. Significa comparar uma afirmação verificável com uma observação registrada. “A tela é acessível” é ampla demais; “o foco alcançou o botão com Tab, no navegador e na versão anotados” pode ser reproduzida. A saída do programa é evidência estrutural: mostra propriedades do arquivo. O relato de teclado, zoom e leitor de tela é evidência manual: mostra uma experiência executada. O inventário e o registro de contestação são evidências de processo: tornam finalidade, autoridade e pendências visíveis. Nenhuma delas substitui as outras. Quando uma verificação não puder ser feita, registre “não testado”, o motivo e o responsável pelo próximo passo; não converta ausência de evidência em aprovação.
Crie numa pasta temporária auditoria-os-742:
auditoria-os-742/
├── jornada.html
├── auditoria.json
├── validar_auditoria.py
└── evidencias.mdEm evidencias.md, registre versão do Python, navegador, zoom, tecnologia assistiva usada, comandos, saída vermelha, saída verde, mutações, limitações e conclusão. Se não houver leitor de tela disponível, declare a limitação; não marque “passou” sem executar.
Passo 1 — construa a falha deliberada
Salve como jornada.html:
<!doctype html>
<html>
<head><meta charset="utf-8"><title>OS 742</title></head>
<body>
<h1>Concluir OS 742</h1>
<textarea id="notes"></textarea>
<div class="button">Concluir ordem</div>
<div id="result">Aguardando</div>
</body>
</html>Há quatro problemas intencionais: idioma da página ausente; campo sem rótulo associado; div que parece botão, mas não é controle nativo; e mensagem sem região de status para tecnologia assistiva.
Salve como auditoria.json. JSON é um formato textual para organizar dados em pares como "campo": "valor"; aqui ele funciona como o formulário estruturado da auditoria:
{
"journey": {
"keyboard_completed": false,
"keyboard_evidence": "botao nao recebe foco",
"zoom_200_completed": false,
"zoom_200_evidence": "nao testado",
"screen_reader_feedback": false,
"screen_reader_evidence": "resultado nao anunciado"
},
"data_inventory": [
{
"field": "technician_name",
"purpose": "atribuir a ordem",
"necessary": true,
"access_roles": ["despacho", "supervisao"],
"retention_days": 365,
"legal_basis_status": "a validar",
"legal_review_owner": "encarregado simulado"
},
{
"field": "exact_gps",
"purpose": "",
"necessary": false,
"access_roles": ["todos"],
"retention_days": 0,
"legal_basis_status": "desconhecida",
"legal_review_owner": ""
}
],
"automated_decision": {
"purpose": "recomendar prioridade da OS",
"ai_disclosed": false,
"human_can_change": false,
"appeal_channel": "",
"response_days": 0,
"group_review_documented": false,
"known_limitations": []
}
}Os valores são didáticos, não política de retenção ou conclusão jurídica. a validar registra pendência; não significa autorização de tratamento.
Passo 2 — use um validador estreito e explicável
Salve como validar_auditoria.py:
import json
import sys
from html.parser import HTMLParser
from pathlib import Path
class StructureAudit(HTMLParser):
def __init__(self):
super().__init__()
self.has_lang = False
self.buttons = 0
self.control_ids = set()
self.label_targets = set()
self.status_regions = 0
def handle_starttag(self, tag, attrs):
values = dict(attrs)
if tag == "html" and values.get("lang"):
self.has_lang = True
if tag == "button":
self.buttons += 1
if tag in {"input", "textarea", "select"} and values.get("id"):
self.control_ids.add(values["id"])
if tag == "label" and values.get("for"):
self.label_targets.add(values["for"])
if values.get("role") == "status" or values.get("aria-live"):
self.status_regions += 1
def validate(html_path="jornada.html", audit_path="auditoria.json"):
findings = []
parser = StructureAudit()
parser.feed(Path(html_path).read_text(encoding="utf-8"))
data = json.loads(Path(audit_path).read_text(encoding="utf-8"))
if not parser.has_lang:
findings.append("HTML: idioma da pagina ausente")
if parser.buttons < 1:
findings.append("HTML: nenhuma acao usa button nativo")
if not parser.control_ids.issubset(parser.label_targets):
findings.append("HTML: existe controle sem label associado")
if parser.status_regions < 1:
findings.append("HTML: feedback sem regiao de status")
journey = data.get("journey", {})
for key in ("keyboard", "zoom_200", "screen_reader_feedback"):
completed_key = f"{key}_completed" if key != "screen_reader_feedback" else key
evidence_key = f"{key}_evidence" if key != "screen_reader_feedback" else "screen_reader_evidence"
if journey.get(completed_key) is not True:
findings.append(f"JORNADA: {key} nao concluido")
if not str(journey.get(evidence_key, "")).strip():
findings.append(f"JORNADA: {key} sem evidencia")
inventory = data.get("data_inventory", [])
if not inventory:
findings.append("DADOS: inventario vazio")
for item in inventory:
field = item.get("field", "sem_nome")
if not str(item.get("purpose", "")).strip():
findings.append(f"DADOS: {field} sem finalidade")
if item.get("necessary") is not True:
findings.append(f"DADOS: {field} nao necessario deve ser removido")
if not item.get("access_roles"):
findings.append(f"DADOS: {field} sem papeis de acesso")
if not isinstance(item.get("retention_days"), int) or item["retention_days"] <= 0:
findings.append(f"DADOS: {field} sem retencao positiva no cenario")
if not str(item.get("legal_review_owner", "")).strip():
findings.append(f"DADOS: {field} sem responsavel por revisao legal")
decision = data.get("automated_decision", {})
if decision.get("ai_disclosed") is not True:
findings.append("IA: participacao automatizada nao informada")
if decision.get("human_can_change") is not True:
findings.append("IA: revisor humano sem autoridade")
if not str(decision.get("appeal_channel", "")).strip():
findings.append("IA: canal de contestacao ausente")
if not 1 <= decision.get("response_days", 0) <= 30:
findings.append("IA: prazo de resposta ausente ou fora do exercicio")
if decision.get("group_review_documented") is not True:
findings.append("IA: revisao de erros por grupo/contexto nao documentada")
if not decision.get("known_limitations"):
findings.append("IA: limitacoes conhecidas ausentes")
for finding in findings:
print("FAIL:", finding)
if findings:
print(f"RESULTADO: REPROVADO ({len(findings)} achados)")
return 1
print("RESULTADO: APROVADO NO ESCOPO ESTRUTURAL DO LABORATORIO")
return 0
if __name__ == "__main__":
raise SystemExit(validate())O analisador de HTML registra apenas cinco sinais estruturais. A função validate compara esses sinais e o JSON com o contrato do exercício. Cada achado informa camada e causa. O programa não calcula contraste, não opera teclado, não executa leitor de tela e não interpreta a LGPD; por isso exige evidência manual e declara seu limite.
Execute:
python validar_auditoria.pyResultado esperado: código de saída 1 e várias linhas FAIL. Se aparecer aprovado, confirme que está na pasta certa e que não substituiu os arquivos iniciais.
Passo 3 — realize a auditoria humana
Abra jornada.html no navegador. Sem mouse, pressione Tab, Shift+Tab, Enter e Espaço: a ação não recebe foco. Aumente o zoom para 200%; registre se conteúdo continua visível, sem sobreposição ou rolagem bidimensional desnecessária. Use o leitor de tela disponível e verifique nome do campo, papel da ação e anúncio do resultado.
Não marque uma etapa pelo resultado desejado. Registre navegador, versão, sequência de teclas, observado e limitação. A orientação oficial do W3C sobre avaliação explica por que ferramenta e avaliação humana se complementam.
Passo 4 — corrija interface, dados e autoridade
Substitua jornada.html por:
<!doctype html>
<html lang="pt-BR">
<head><meta charset="utf-8"><title>Concluir OS 742</title></head>
<body>
<main>
<h1>Concluir OS 742</h1>
<label for="notes">Observações técnicas</label>
<textarea id="notes"></textarea>
<button type="button">Concluir ordem</button>
<div id="result" role="status" aria-live="polite">Aguardando</div>
</main>
</body>
</html>O botão nativo recebe semântica e operação de teclado do navegador. O rótulo aponta para o campo. role="status" e aria-live permitem anunciar atualização; ainda será necessário testar comportamento real quando houver JavaScript.
Substitua auditoria.json pelo artefato corrigido:
{
"journey": {
"keyboard_completed": true,
"keyboard_evidence": "Tab alcancou o button; Enter e Espaco foram testados no prototipo",
"zoom_200_completed": true,
"zoom_200_evidence": "conteudo permaneceu legivel no navegador registrado",
"screen_reader_feedback": true,
"screen_reader_evidence": "campo, button e regiao de status foram anunciados no teste registrado"
},
"data_inventory": [
{
"field": "technician_id",
"purpose": "atribuir a ordem ao tecnico autorizado",
"necessary": true,
"access_roles": ["despacho", "supervisao"],
"retention_days": 365,
"legal_basis_status": "nao determinada neste laboratorio",
"legal_review_owner": "encarregado de dados simulado"
},
{
"field": "facility_code",
"purpose": "rotear a ordem para a unidade correta sem GPS exato",
"necessary": true,
"access_roles": ["despacho", "manutencao"],
"retention_days": 365,
"legal_basis_status": "nao determinada neste laboratorio",
"legal_review_owner": "encarregado de dados simulado"
}
],
"automated_decision": {
"purpose": "recomendar prioridade da OS, sem concluir ou cancelar automaticamente",
"ai_disclosed": true,
"human_can_change": true,
"appeal_channel": "/revisao-prioridade",
"response_days": 2,
"group_review_documented": true,
"known_limitations": [
"sensores antigos possuem mais dados ausentes",
"recomendacao se abstém quando faltam sinais essenciais"
]
}
}O GPS exato foi removido porque não havia finalidade no cenário; facility_code oferece precisão menor para roteamento. Os 365 dias são hipótese didática e precisam de política real. nao determinada neste laboratorio impede que o exercício finja parecer jurídico. Antes de produção, o responsável valida base, prazo, acesso, compartilhamento e direitos.
Execute novamente. Esperado:
RESULTADO: APROVADO NO ESCOPO ESTRUTURAL DO LABORATORIOCódigo de saída deve ser 0.
Passo 5 — provoque regressões
Primeira mutação: troque temporariamente <button type="button"> por <div>. Execute; espere HTML: nenhuma acao usa button nativo. Restaure e confirme verde.
Segunda mutação: mude human_can_change para false. Execute; espere IA: revisor humano sem autoridade. Restaure e confirme verde. Se uma mutação não falhar, o validador não protege o critério anunciado.
Esses testes não provam que toda decisão humana será boa. Provam que o artefato não pode declarar supervisão enquanto registra ausência de autoridade.
Diagnóstico e recuperação
| Sintoma | Hipótese | Verificação | Ação |
|---|---|---|---|
FileNotFoundError |
pasta ou nome errado | Get-Location e lista de arquivos |
execute na pasta temporária correta |
JSONDecodeError |
vírgula ou aspas inválidas | linha indicada pelo erro | corrija sintaxe sem apagar o campo |
| aprovado sem teste manual | booleanos copiados como verdade | leia evidencias.md |
volte para false até executar |
| ferramenta automática e teclado discordam | limite da automação | reproduza sequência de teclas | priorize bloqueio observado e investigue |
| campo possui “finalidade” vaga | justificativa criada depois da coleta | tente removê-lo | reduza ou elimine; escale decisão |
| base legal continua pendente | laboratório não é análise jurídica | veja responsável e status | não libere produção; encaminhe responsável |
Pacote de evidências e aceite
O pacote final contém os três arquivos, evidencias.md, saída vermelha, saída verde e duas mutações falhando. Registre ainda stakeholder afetado, impacto, correção, critério WCAG relacionado e teste de regressão. Priorize incapacidade de concluir a jornada, não quantidade bruta de avisos.
O laboratório passa quando:
- teclado, zoom e tecnologia assistiva têm evidência honesta ou limitação explícita;
- estrutura mínima passa e falha diante das mutações;
- localização exata sem finalidade foi removida;
- cada dado restante possui finalidade, necessidade, acesso, retenção hipotética e responsável;
- pendência jurídica permanece visível e bloqueia produção até validação competente;
- participação da IA é informada, limitações estão documentadas e há abstenção;
- contestação possui canal, prazo e revisor capaz de alterar o resultado;
- outra pessoa reproduz comandos e conclusões sem dados reais.
Transferência: adapte o inventário a um chamado de recursos humanos ou atendimento ao cliente. Quais stakeholders, danos e dados mudam? Quais critérios — finalidade, acesso, evidência, contestação — permanecem?
Recuperação ativa: por que o validador não certifica WCAG? O que a saída verde prova? Por que a validar é melhor que base inventada? Qual mutação demonstra supervisão simbólica? Como minimização mudou GPS para código de unidade?
Conclusão e limpeza
A conclusão correta não é “o sistema é ético” nem “está em conformidade legal”. É: os controles estruturais deste cenário passaram; a jornada manual possui as evidências declaradas; uma coleta sem finalidade foi removida; a supervisão e o recurso foram documentados; e decisões jurídicas, testes completos de acessibilidade e avaliação contínua de vieses permanecem com responsáveis identificados.
Para limpar, preserve evidencias.md sem dados reais se fizer parte do portfólio. Depois remova somente a pasta temporária auditoria-os-742 que você criou e cujo caminho confirmou. Não use caminho amplo, glob ou variável não resolvida.
Comprove o que você aprendeu
Responda todas as questões. O gabarito comentado só aparece depois do envio.