Artefacto e âmbito
Este laboratório usa o XSD público head.001.001.04, obtido no catálogo oficial, sem alterações. O código Python e as instâncias são originais DR; o schema é um artefacto externo identificado no registo de proveniência. O objetivo é validar cabeçalhos AppHdr sintéticos, não enviar pagamentos. O catálogo consultado apresenta a versão V04, enquanto a página descritiva BAH ainda refere V03; para o ensaio, o ficheiro e o seu targetNamespace determinam a versão. A versão adotada por uma comunidade real continua a depender do contrato aplicável. Não substituir esse acordo pela data mais recente de uma página.
Preparar uma execução repetível
Guardar run.py e head.001.001.04.xsd na mesma pasta. O download oficial encontra-se nas referências da aula. O script compara SHA-256 com o valor registado antes de chamar xmllint; uma diferença interrompe o ensaio para investigação. É necessário Python 3.13 e xmllint disponível no sistema, ou definir DR_XMLLINT com o caminho do executável. A execução observada usou Python 3.13.1 e libxml2 2.9.13. O programa não instala ferramentas nem descarrega ficheiros durante o teste. Só processa fixtures sintéticas geradas no próprio código; não constitui recomendação deste runtime para um gateway de produção.
Fronteiras realmente executadas
Prever cada resultado antes de executar. Retirar Fr, inverter Fr e To, deixar BizMsgIdr vazio ou usar 36 caracteres causa rejeição no schema escolhido. O caso de 35 caracteres passa. O par distingue limite inclusivo e erro de geração sem truncar identidades automaticamente. Outra fixture emite simultaneamente FIId e OrgId em Fr e falha a escolha estrutural. Uma data impossível também falha. O relatório guarda código de saída e diagnóstico para cada instância. O executor trata erros da ferramenta separadamente: schema ausente ou falha de execução não pode contar como rejeição esperada de uma mensagem.
Schema aprovado, contrato ainda incompleto
Algumas fixtures passam precisamente para revelar limites. MsgDefIdr é texto limitado no XSD e aceita um identificador arbitrário; a existência e adequação da definição não ficam demonstradas. CreDt sem fuso e FinInstnId vazio também passam os casos de base ensaiados. O perfil fictício DR-BAH-A acrescenta fuso explícito, identificador acordado e BICFI nas duas partes. Essas regras são didáticas, não requisitos universais ISO. O código externo de cinco caracteres passa a restrição de comprimento sem consultar uma lista. Da mesma forma, os BIC sintéticos que respeitam o padrão não foram confirmados em qualquer diretório ou comunidade.
Estrutura não prova autenticidade
Sgntr contém um wildcard no namespace de assinatura com processContents lax. No ensaio, sem schema de assinatura carregado, um elemento Signature vazio nesse namespace passa a validação do cabeçalho. Não houve operação criptográfica, certificado, chave ou cadeia de confiança verificada. Mudar para validação estrutural strict também não seria, por si, verificação criptográfica. Este caso ajuda o PM a pedir a evidência correta ao responsável de segurança: definir ponto de verificação, material de confiança, dados abrangidos e resultado esperado. O laboratório limita-se a demonstrar a fronteira entre presença estrutural e a garantia indevidamente inferida.
Interpretar e entregar evidência
Reservar dez minutos para preparar ferramentas e ficheiro, quinze para prever resultados, vinte para executar e comparar, e quinze para explicar duas aceitações insuficientes. O resultado inclui 22 casos XSD e quinze verificações de perfil e planeamento, totalizando 37. Conservar hash do script, schema, fixtures, versão do validador e diagnósticos. O comando executa --schema sobre ficheiro local, com catálogos desativados e --nonet neste runtime. Estas opções não equivalem a uma análise de segurança de parser. O fecho correto é evidência local dos casos registados; MDR completo, corpo financeiro, assinatura, contraparte e aceitação operacional exigem validações próprias.
"""Original DR BAH schema workshop. Python 3.13 + xmllint.
Obtain head.001.001.04.xsd from https://www.iso20022.org/message/23104/download
Place the unchanged file next to this script. Run:
python3 run.py --output evidence.json
Trusted generated fixtures only. No messages sent or cryptographic checks.
"""
import argparse
import hashlib
import json
import os
import re
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
from xml.etree import ElementTree as ET
SCHEMA_SHA = '73d68e98ec079806a23f37494ed47662a74cebfae91807bfbaacda3728d65796'
NS = 'urn:iso:std:iso:20022:tech:xsd:head.001.001.04'
MSG = 'pacs.008.001.08'
BASE = f'''<AppHdr xmlns="{NS}">
<Fr><FIId><FinInstnId><BICFI>DRXXZZ00</BICFI></FinInstnId></FIId></Fr>
<To><FIId><FinInstnId><BICFI>DRYYZZ00</BICFI></FinInstnId></FIId></To>
<BizMsgIdr>DR-HEADER-001</BizMsgIdr><MsgDefIdr>{MSG}</MsgDefIdr>
<CreDt>2026-10-03T10:00:00Z</CreDt></AppHdr>'''
# Synthetic identifiers are not verified directory members or real participants.
checks = []
def check(name, actual, expected):
assert actual == expected, (name,actual,expected)
checks.append(dict(name=name,actual=actual,expected=expected,passed=True))
def profile_errors(xml):
"""Invented DR-BAH-A profile, separate from base schema validation."""
root=ET.fromstring(xml);n={'h':NS};errors=[]
if root.findtext('h:MsgDefIdr',namespaces=n)!=MSG: errors.append('message contract')
date=root.findtext('h:CreDt',default='',namespaces=n)
if not re.search(r'(Z|[+-][0-9]{2}:[0-9]{2})$',date):errors.append('explicit timezone')
for party in ('Fr','To'):
if not root.findtext(f'h:{party}/h:FIId/h:FinInstnId/h:BICFI',namespaces=n):errors.append(party+' identification')
return errors
def route_gaps(required, evidence, bundle):
# Coverage under an original fictional release contract; not statistical assurance.
observed={x['route'] for x in evidence if x['bundle']==bundle and x['passed']}
return sorted(set(required)-observed)
def main():
parser=argparse.ArgumentParser();parser.add_argument('--output',required=True);args=parser.parse_args()
schema=Path(__file__).with_name('head.001.001.04.xsd')
if hashlib.sha256(schema.read_bytes()).hexdigest()!=SCHEMA_SHA:raise SystemExit('Unexpected schema bytes; investigate before updating the pin.')
exe=os.environ.get('DR_XMLLINT') or shutil.which('xmllint')
if not exe:raise SystemExit('xmllint is required; no schema checks were performed.')
version=subprocess.run([exe,'--version'],capture_output=True,text=True,check=True)
env={**os.environ,'XML_CATALOG_FILES':'','SGML_CATALOG_FILES':''}
root=ET.fromstring(schema.read_bytes());x='{http://www.w3.org/2001/XMLSchema}'
assert not any(root.findall(x+name) for name in ('import','include','redefine'))
fr='<Fr><FIId><FinInstnId><BICFI>DRXXZZ00</BICFI></FinInstnId></FIId></Fr>'
to='<To><FIId><FinInstnId><BICFI>DRYYZZ00</BICFI></FinInstnId></FIId></To>'
nozone=BASE.replace('10:00:00Z','10:00:00');othermsg=BASE.replace(MSG,'not-a-message-definition')
emptyid=BASE.replace('<BICFI>DRXXZZ00</BICFI>','')
cases=[
('baseline',BASE,True),
('required sender absent',BASE.replace(fr,''),False),
('sender receiver sequence reversed',BASE.replace(fr+'\n'+to,to+'\n'+fr),False),
('empty message identity',BASE.replace('DR-HEADER-001',''),False),
('thirty six character identity',BASE.replace('DR-HEADER-001','A'*36),False),
('thirty five character identity',BASE.replace('DR-HEADER-001','A'*35),True),
('message identifier is only bounded text',othermsg,True),
('invalid calendar date',BASE.replace('2026-10-03','2026-02-30'),False),
('timezone absent at base schema',nozone,True),
('prefix alias',re.sub(r'<(/?)([A-Za-z][A-Za-z0-9]*)',r'<\1h:\2',BASE).replace('xmlns=','xmlns:h='),True),
('different header namespace',BASE.replace('head.001.001.04','head.001.001.03'),False),
('both sender choice branches',BASE.replace('</Fr>','<OrgId/></Fr>'),False),
('empty financial institution at base schema',emptyid,True),
('malformed BIC shape',BASE.replace('DRXXZZ00','bad'),False),
('unexpected root child',BASE.replace('</AppHdr>','<Extra/></AppHdr>'),False),
('lax signature structure is not verification',BASE.replace('</AppHdr>','<Sgntr><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"/></Sgntr></AppHdr>'),True),
('signature wrong namespace',BASE.replace('</AppHdr>','<Sgntr><Signature xmlns="urn:dr:wrong"/></Sgntr></AppHdr>'),False),
('boolean true',BASE.replace('</AppHdr>','<PssblDplct>true</PssblDplct></AppHdr>'),True),
('boolean yes',BASE.replace('</AppHdr>','<PssblDplct>yes</PssblDplct></AppHdr>'),False),
('external code length five',BASE.replace('</BICFI>','</BICFI><ClrSysMmbId><ClrSysId><Cd>ZZZZZ</Cd></ClrSysId><MmbId>DR-MEMBER</MmbId></ClrSysMmbId>',1),True),
('external code length six',BASE.replace('</BICFI>','</BICFI><ClrSysMmbId><ClrSysId><Cd>ZZZZZZ</Cd></ClrSysId><MmbId>DR-MEMBER</MmbId></ClrSysMmbId>',1),False),
('sender choice absent',BASE.replace(fr,'<Fr/>'),False),
]
results=[]
with tempfile.TemporaryDirectory(prefix='dr-bah-schema-') as td:
for index,(name,xml,expected) in enumerate(cases):
p=Path(td)/f'case-{index:02}.xml';p.write_text(xml)
run=subprocess.run([exe,'--nonet','--noout','--schema',str(schema),str(p)],env=env,capture_output=True,text=True,timeout=10)
if run.returncode not in (0,3):raise AssertionError((name,'unexpected tool failure',run.returncode,run.stderr))
check('XSD '+name,run.returncode==0,expected)
results.append(dict(name=name,xml=xml,expectedValid=expected,exitCode=run.returncode,diagnostic=run.stderr.replace(td,'<temporary>'),sha256=hashlib.sha256(xml.encode()).hexdigest()))
check('custom profile baseline',profile_errors(BASE),[])
check('custom profile requires timezone',profile_errors(nozone),['explicit timezone'])
check('custom profile binds message identifier',profile_errors(othermsg),['message contract'])
check('custom profile requires sender identifier',profile_errors(emptyid),['Fr identification'])
routes=['A-in','A-out','B-in','B-out']
evidence=[dict(route=p,bundle='release-1',passed=True) for p in routes]
check('complete route evidence',route_gaps(routes,evidence,'release-1'),[])
check('outbound gap despite other routes',route_gaps(routes,evidence[:-1],'release-1'),['B-out'])
check('duplicate route cannot fill gap',route_gaps(routes,evidence[:-1]+[evidence[0]],'release-1'),['B-out'])
check('changed bundle needs applicable evidence',route_gaps(routes,evidence,'release-2'),sorted(routes))
failed=[{**e,'passed':e['route']!='A-in'} for e in evidence]
check('failed route stays uncovered',route_gaps(routes,failed,'release-1'),['A-in'])
# Fictional 90 minute window; reserve 15 rollback +10 reconciliation.
check('latest safe rollback decision minute',90-15-10,65)
check('remaining after decision at minute 70',90-70,20)
check('late decision cannot fit reserved recovery',90-70>=15+10,False)
check('candidate at minute 60 leaves five minute reserve',90-60-15-10,5)
activation=lambda tested,approved,contract,staffed:all((tested,approved,contract,staffed))
check('tests alone do not authorize activation',activation(True,False,True,True),False)
check('agreed fictional activation conditions',activation(True,True,True,True),True)
output=dict(scope='Actual local XSD validation of original synthetic BAH headers against pinned public head.001.001.04; custom profile and release arithmetic only. No full-message, MDR, community, directory, signature verification, settlement or real cutover acceptance.',python=sys.version.split()[0],validator=(version.stdout+version.stderr).strip(),schemaSha256=SCHEMA_SHA,runnerSha256=hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),passed=len(checks),checks=checks,schemaCases=results)
Path(args.output).write_text(json.dumps(output,indent=2)+'\n');print(json.dumps({'passed':len(checks),'schemaCases':len(cases),'schemaSha256':SCHEMA_SHA}))
if __name__=='__main__':main()
Caso: o mesmo cabeçalho passa o XSD e falha duas condições do perfil didático. O relatório deve conservar ambas as observações e identificar a regra responsável.
Armadilhas comuns
Schema verde como aceitação integral; assinatura presente como autenticidade; padrões de texto como diretórios; erro de ferramenta como rejeição funcional.
Tópicos relacionados: Mensagens, cabeçalhos e estados · Versões, extensões e interoperabilidade · Testes, diagnóstico e operação
Fixar artefactos e executar limites positivos e negativos, mantendo separadas estrutura, perfil, semântica, autenticidade e efeito de negócio.
Referência: BusinessApplicationHeaderV04 XSD · DR ISO20022 fundamentals professional assessment2026.10