#!/usr/bin/env python3 """Project declared OpenAPI security requirements into practical operation coverage.""" from __future__ import annotations import argparse from pathlib import Path import yaml def execute(doc): op_contract={};ops={} for c in doc.get('contracts',[]) or []: for o in c.get('operations',[]) or []:ops[o['id']]=o;op_contract[o['id']]=c['id'] profiles=doc.get('security',[]) or [];by_target={} for p in profiles:by_target.setdefault(p.get('applies_to'),[]).append(p) override={} for a in doc.get('assertions',[]) or []: if a.get('assertion_kind')=='property' and a.get('predicate')=='fiam.property.security-override' and a.get('polarity')=='asserted':override[a.get('subject')]=a.get('value') rows=[] for oid in sorted(ops): own=by_target.get(oid,[]);globalp=by_target.get(op_contract.get(oid),[]);ov=override.get(oid) if ov=='none':status='explicitly-unprotected';effective=[] elif ov=='anonymous-alternative':status='anonymous-access-allowed';effective=own if own else globalp elif own:status='operation-specific';effective=own elif globalp:status='inherited-contract-security';effective=globalp else:status='no-declared-security';effective=[] alternatives={} for p in effective: alt=(p.get('facets') or {}).get('requirement_alternative',1);alternatives.setdefault(alt,[]).append({'profile':p['id'],'authentication':p.get('authentication'),'scheme':(p.get('facets') or {}).get('openapi_security_scheme'),'scopes':(p.get('facets') or {}).get('required_scopes',[])}) rows.append({'operation_ref':oid,'contract_ref':op_contract.get(oid),'classification':status,'alternatives':[{'alternative':k,'all_of':v} for k,v in sorted(alternatives.items())],'runtime_observability':'not-inferred-from-generic-OTLP'}) summary={} for r in rows:summary[r['classification']]=summary.get(r['classification'],0)+1 return {'lens':'fiam.lens.security-coverage','model':doc.get('instance',{}).get('id'),'summary':dict(sorted(summary.items())),'operations':rows,'note':'Generic OTLP does not prove authentication presence/absence; this lens reports declared coverage only unless an explicit security telemetry source is added.'} def main(): ap=argparse.ArgumentParser(description=__doc__);ap.add_argument('model',type=Path);ap.add_argument('-o','--output',type=Path,required=True);args=ap.parse_args();r=execute(yaml.safe_load(args.model.read_text()));args.output.write_text(yaml.safe_dump(r,sort_keys=False,allow_unicode=True,width=120));print(yaml.safe_dump(r['summary'],sort_keys=False).strip()) if __name__=='__main__':main()