← Risco operacional: fundamentos, controlos e decisões
07 / 10 · 60 MIN

Evidência de controlos: população e eficácia

Construir uma conclusão rastreável a partir de critérios, observações e lacunas de cobertura.

Começar pela afirmação que precisa de prova

Num projeto fictício de fundos, a equipa quer afirmar que mudanças sem aprovação são bloqueadas antes da execução. Escreve essa afirmação antes de procurar documentos. Define ação, sistema, período, população e resultado esperado. Uma presença em formação mostra participação; um procedimento mostra desenho; um pedido recusado sob condições conhecidas observa comportamento. Estes elementos podem complementar-se. A secção 2.4 de NIST SP 800-53A revisão 5 distingue exame, entrevista e teste. A aula aplica essa distinção a um exercício original, sem transformar o código num programa completo de avaliação NIST ou numa auditoria bancária.

Definir a população antes da seleção

O inventário contém duzentas mudanças: cento e oitenta normais e vinte urgentes. A seleção contém vinte normais, todas satisfatórias. Observaste dez por cento da população e nenhum caso urgente. O resultado pode ser útil, mas não demonstra o mecanismo que o turno usa numa emergência. Escolher só os registos mais fáceis introduz uma limitação que deve aparecer no relatório. Justifica seleção, diferenças de mecanismo, criticidade e período. O exemplo não calcula confiança estatística nem prescreve vinte como dimensão universal de amostra. Uma população incompleta também compromete a seleção, mesmo que todos os seus elementos tenham sido verificados.

Reconciliar identidade e proveniência

Quatro linhas não provam quatro execuções distintas. Se esperas a,b,c,d e recebes a,b,b,c, falta d e b aparece duas vezes. Conserva referências para reconstruir a origem e separa repetição idêntica de conflito. Se a mesma execução tem pass e fail, a ordem das linhas não decide qual resultado é válido. Procura versão, reexecução ou transformação que explique a divergência. Até lá, mantém a exceção visível. O modelo local identifica ausentes, inesperados, cópias e conflitos; não escolhe automaticamente um resultado favorável nem apaga registos de um sistema real.

Separar observado, falhado e desconhecido

Cem controlos estavam previstos: oitenta e cinco têm pass, cinco fail e dez não têm evidência. A cobertura de resultados conhecidos é noventa por cento; a fração de pass demonstrado sobre o previsto é oitenta e cinco. Dividir oitenta e cinco por noventa responde a outra pergunta: resultado entre observados. Pode ser apresentado, desde que a população e os dez desconhecidos continuem visíveis. Não preenchas silenciosamente os ausentes com o estado de ontem. Também não declares cem falhas para compensar incerteza. Mostra o que sabes, o que falta e quem vai recuperar a observação.

Reavaliar após a alteração

Uma regra de aprovação foi alterada depois do ensaio. O nome do controlo ficou igual, mas isso não demonstra que o comportamento se manteve. Identifica a parte afetada e a evidência adicional necessária. Uma avaliação externa pode contribuir se o âmbito, período e configuração forem pertinentes; uma nova data na apresentação não atualiza o ensaio. No caso de duas contas da mesma pessoa, a instalação da correção é um marco técnico. Se ambas continuam a aprovar, a independência pretendida continua sem demonstração. Mantém entrega e eficácia como estados distintos, com responsável e próximo critério de aceitação.

Executar e explicar os limites

Guarda o código seguinte como run.py e executa python3 run.py --output evidence.json. Usa apenas a biblioteca padrão e dados inventados. Analisa cada verificação antes de interpretar passed como resultado: esse campo conta comparações determinísticas, não controlos bancários aprovados. O programa trabalha sobre listas em memória e grava um ficheiro local; não lê dados de clientes ou contacta serviços. Explica depois que evidência faltaria para avaliar o controlo num ambiente autorizado. Entrega uma matriz com objetivo, população, seleção, resultados, lacunas e ação. A revisão humana e a observação do mecanismo real continuam atividades separadas.

"""Original fictional evidence model; no bank data, capital or compliance assessment.
Run: python3 run.py --output evidence.json
"""
import argparse
import hashlib
import json
import platform
from fractions import Fraction
from pathlib import Path


def ratio(numerator, denominator):
    if denominator == 0:
        return None
    return float(Fraction(numerator, denominator) * 100)


def reconcile(expected, observations):
    by_id = {}
    conflicts = []
    duplicates = 0
    for row in observations:
        key = row['id']
        if key in by_id:
            if by_id[key] != row:
                conflicts.append(key)
            else:
                duplicates += 1
        else:
            by_id[key] = row
    return dict(missing=sorted(set(expected)-set(by_id)),
                unexpected=sorted(set(by_id)-set(expected)),
                conflicts=sorted(set(conflicts)), duplicate_copies=duplicates)


def loss_summary(events, recoveries):
    # One recognized event per stable ID; identical copies collapse, conflicts fail.
    indexed = {}
    for row in events:
        if row['id'] in indexed and indexed[row['id']] != row:
            raise ValueError('conflicting event version')
        indexed[row['id']] = row
    recovery_ids = set()
    received = 0
    for row in recoveries:
        if row['id'] in recovery_ids:
            raise ValueError('duplicate recovery identifier')
        recovery_ids.add(row['id'])
        if row['event'] not in indexed:
            raise ValueError('unmatched recovery')
        if row['status'] == 'received':
            received += row['amount']
    gross = sum(r['amount'] for r in indexed.values() if r['kind'] == 'loss')
    return dict(events=len(indexed), gross=gross, received=received, net=gross-received)


def run():
    checks = []
    def check(name, actual, expected):
        assert actual == expected, (name, actual, expected)
        checks.append(dict(name=name, actual=actual, expected=expected, passed=True))
    check('selected normal records pass', ratio(20,20), 100)
    check('normal stratum sample coverage', ratio(20,180), float(Fraction(100,9)))
    check('emergency stratum sample coverage', ratio(0,20), 0)
    check('population sample coverage', ratio(20,200), 10)
    check('no observed executions is not perfect effectiveness', ratio(0,0), None)
    statuses = ['pass']*85 + ['fail']*5 + ['unknown']*10
    check('complete control evidence coverage', ratio(sum(s!='unknown' for s in statuses),len(statuses)),90)
    check('demonstrated pass fraction of scheduled population', ratio(statuses.count('pass'),len(statuses)),85)
    check('failure fraction among observed records', ratio(5,90),float(Fraction(50,9)))
    check('unknown records retained separately', statuses.count('unknown'),10)
    expected = ['a','b','c','d']
    rows = [{'id':'a','result':'pass'},{'id':'b','result':'pass'},{'id':'b','result':'pass'},{'id':'c','result':'fail'}]
    r = reconcile(expected, rows)
    check('raw row count can equal population', len(rows), len(expected))
    check('duplicate cannot fill missing observation', r['missing'],['d'])
    check('identical repeated evidence counted', r['duplicate_copies'],1)
    check('conflicting result isolated', reconcile(expected,rows+[{'id':'b','result':'fail'}])['conflicts'],['b'])
    check('unexpected evidence identity exposed',reconcile(expected,rows+[{'id':'x','result':'pass'}])['unexpected'],['x'])
    check('stable definition first period defect percent', ratio(10,1000),1)
    check('stable definition second period defect percent', ratio(15,3000),0.5)
    check('absolute exception count still rises',15-10,5)
    check('combined pass ratio uses combined counts',ratio(90+50,100+1000),float(Fraction(1400,110)))
    check('unweighted average differs', (90+5)/2,47.5)
    check('stale observations cannot prove current period', 60<=15,False)
    events=[dict(id='e1',kind='loss',amount=12000),dict(id='e2',kind='loss',amount=3000),dict(id='e1',kind='loss',amount=12000),dict(id='n1',kind='near-miss',amount=50000)]
    recovery=[dict(id='r1',event='e1',amount=4000,status='received'),dict(id='r2',event='e1',amount=2000,status='pending')]
    result=loss_summary(events,recovery)
    check('distinct event records include near miss',result['events'],3)
    check('gross ignores duplicate and potential near miss',result['gross'],15000)
    check('received recovery excludes pending claim',result['received'],4000)
    check('net observed amount retains gross lineage',result['net'],11000)
    for name,ev,rec,message in [
        ('conflicting event versions fail',events+[dict(id='e1',kind='loss',amount=13000)],recovery,'conflicting event version'),
        ('duplicate recovery identifiers fail',events,recovery+[recovery[0]],'duplicate recovery identifier'),
        ('unmatched recovery fails',events,[dict(id='r3',event='missing',amount=100,status='received')],'unmatched recovery')]:
        try:loss_summary(ev,rec);value='accepted'
        except ValueError as e:value=str(e)
        check(name,value,message)
    gate=dict(implemented=True,effective=False,owner_acceptance=True)
    check('implementation alone cannot close action',all(gate.values()),False)
    check('all fictional closure criteria demonstrated',all({**gate,'effective':True}.values()),True)
    check('expired exception at exact boundary',10<10,False)
    return dict(runtime=platform.python_version(),scope='Synthetic evidence, ratios and event ledger only; not statistical assurance, an actual RCSA, accounting policy, capital calculation or regulatory submission.',passed=len(checks),checks=checks,runnerSha256=hashlib.sha256(Path(__file__).read_bytes()).hexdigest())

if __name__ == '__main__':
    p=argparse.ArgumentParser();p.add_argument('--output',required=True)
    args=p.parse_args();Path(args.output).write_text(json.dumps(run(),indent=2)+'\n')
NA PRÁTICA

Exemplo: vinte mudanças normais passaram, mas nenhuma urgente foi selecionada. Preserva os resultados e torna explícita a lacuna antes da aceitação do fim de semana.

Armadilhas comuns

Confundir pass na amostra com confiança estatística; contar cópias como cobertura; atualizar datas sem nova observação; fechar eficácia com deployment.

Tópicos relacionados: Avaliação de risco e eficácia de controlos · Eventos, quase incidentes e resposta · Indicadores, reporte e aceitação

Leva esta ideia contigo

A conclusão deve poder ser reconstruída a partir do objetivo, da população e das observações, incluindo aquilo que ficou por demonstrar.

Criar conta

Referência: Assessing Security and Privacy Controls in Information Systems and Organizations · DR operational risk professional assessment2026.10